Alex
Alex

Reputation: 107

How do I trigger a call to dispatch using React Hooks?

I have a save function in a component that makes some calls to an API and changes my local state based on the response. I then want to dispatch an action once AND ONLY ONCE these calls to setState are complete e.g.

const [myData, setMyData] = useState([{id: 0, name: "Alex"}]);

const save = async () => {

    if (someCondition) {
        let response = await axios.get("/someroute");
        if (response.status === 200) {
            setMyData([...myData, response.data])])
        }
    }

    if (someOtherCondition) {
        let response2 = await axios.get("/someotherroute");
        if (response2.status === 200) {
            setMyData([...myData, response2.data])])
        }
    }

    dispatch(myAction(myData));
}

Now I know that useEffect should be the way to go about this. e.g.

useEffect(() => {
    dispatch(myAction(myData));
}, [myData]);

however i'm changing myData at other places in the component, and I only want to dispatch the action once save() has been called and the setMyData calls have finished. I can't think of a condition to check for in useEffect() except maybe adding some kind of flag to myData, like an object saying "Ready to save", but it feels like there should be a simpler way.

Massively appreciate any help on this one!

Upvotes: 1

Views: 2208

Answers (2)

jdnichollsc
jdnichollsc

Reputation: 1569

It's looks like you need to use useReduce instead and then you are able to detect when any of the props change with the useEffect:


const initialState = {
  id: null,
  name: null,
  ...
}
const [state, dispatch] = useReducer((state, action) => {
  switch (action.type) {
    case 'CHANGE-NAME':
      return { ...state, name: action.value }
    default:
      throw new Error('Type not defined')
  }
}, initialState)

useEffect(() => {
  // Notify changes!!
}, [state.name]);


...
const myFun = async () {
  // do something
  dispatch({ type: 'CHANGE-NAME', value: 'Hello world' })
}

Also you can add other props to your initial state, for example if you need a specific flag to detect an action once :)

Regards

Upvotes: 0

Kerem atam
Kerem atam

Reputation: 2787

What do you mean by changing myData at other places? Are you changing myData by fetching it from somewhere? Because if you don't, setMyData will do its job pretty straightforward and fast. So your save function won't need to listen it.

If you change myData as a result of some other fetching and save function should wait it. Then story can little bit complicated. You might like to check middlewares and redux sagas.

I have hard time to understand your scenario and use case; but if state overwriting is your concern during consecutive fetch actions then saga middleware can take care of it.

For example you can create some saga middleware with takeLatest so that it will take last action that dispatched and kill the previous one (not waiting) that ongoing; so you would avoid data overwrite that might occur due side effect. Or similarly you might want to use takeEvery, which will queue every action for setting myData and they will wait each other to end.

check more : https://redux-saga.js.org/

and usage example : https://codesandbox.io/s/385lk3x6v1?file=/src/sagas/index.js

Upvotes: 1

Related Questions