How do I trigger an effect when an action is dispatched that updates state to same as current?

Viewed 102

I have an application that works like this. State contains a target. The user can click on a save button to open a file picker and select a target. The intended behavior is that whenever the user selects a target the application performs a save.

The code below almost works. But unfortunately the save effect does not happen when the user selects the same target as was previously in state.

  const [state, dispatch] = useReducer(reducer, {
    target: "",
  });

  const setTarget = async () => {
    // user interacts with a file picker
    dispatch({ type: SET_TARGET, target});
  }

  useEffect(
    function save() {
      if (state.target) {
        // some side effects happen
      }
    },
    [state.target]
  );

What is a good way to solve this issue using the world of React and Hooks?

1 Answers

You could make value of the target property to be an object instead of just a string. This will work because every time you update the state, it will return a new object for the target property.

Following code snippet provides an example:

const reducer = (state, action) => {
  switch (action.type) {
    case "SET_TARGET":
      return {
        ...state,
        target: { selectedTarget: action.payload }
      };
    default:
      return state;
  }
};

function App() {
  const [state, dispatch] = React.useReducer(reducer, {
    target: { selectedTarget: "" }
  });

  React.useEffect(() => {
    console.log("effect ran");
    console.log(state.target);
  }, [state.target]);

  const handleClick = () => {
    dispatch({ type: "SET_TARGET", payload: "New Target" });
  };

  return (
     <button onClick={handleClick}>Update Target</button>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related