Search code examples
react-reduxredux-saga

How do you get data back from a react-redux store?


Using React-Redux

I have a select list that when the user chooses one of the options, a item is created and placed in the database (if it matters, the reason its a select box is that there are multiple variations of the same item and what variation is important).

This is working correctly.

My problem is that I am not sure how I can get the id of the new item out of the redux store.

And just for chuckles, the prior developer set all this up with sagas. So I am still coming up to speed on how it all works together.

So when the select box is checked, the function checkFunction is called that calls the function createItem in the saga file. These functions are below:

in Repositories.jsx

  checkFunction = (data) => {
    const {createItem} = this.props;
    // data holds the info that we need to send to the action
    const created = createItem(data);
    // once data comes back, redirect the user ot the details of the created item
    // need id of created item
    // navigateTo(`/item-details/${created.id}`);
  }

in Repositories.saga.js

export function* createItem(action) {
  try {
    const {payload: newItem} = action;
    // call api to create item
    const created = yield call(requestAPI.post, ITEMS_URL, newItem);
    // send message that its been done
    yield put(actions.repositories.createItem.ok(created));
    // send message to refresh item list
    yield put(actions.inventories.fetchItems.start());
  } catch (e) {
    yield put(actions.repositories.createItem.fail(e));
  }
}

I don't understand how to return the id of the created item once its created. I feel like I am missing something basic here. Can anyone point me in the right direction?


Solution

  • Actually getting data from saga back to react component is not trivial. There are multiple approaches to do what you need although each has its downside.

    1. Call navigateTo in the saga.

    export function* createItem(action) {
        ...
        const created = yield call(requestAPI.post, ITEMS_URL, newItem);
        yield call(navigateTo, `/item-details/${created.id}`)
    }
    

    This would be my recommended solution if you can get the navigateTo function into the saga. Navigation is a side effect and sagas are there to deal with side effects. Make sure to use the call effect, changing the url by directly calling the function can lead to some issues.

    2. Store the latest created item id in redux store

    In your reducer, when action actions.repositories.createItem.ok(created) is dispatched, store the created item info and then send the latest created item to the component. Finally you can use componentDidUpdate or useEffect to call navigateTo when the prop changes.

    render() {
        const created = Redux.useSelector(state => state.created);
        useEffect(() => navigateTo(`/item-details/${created.id}`), [created])
        ...
    }
    

    This has the disadvantage that the component will rerender becuase of the changed created value.

    3. Send callback in the createItem action

    You can put a function into your action and then call it from the saga and essentially using it as a callback.

    Component:

    checkFunction = (data) => {
        const {createItem} = this.props;
        // data holds the info that we need to send to the action
        const created = createItem(data, (created) => {
            navigateTo(`/item-details/${created.id}`);
        });
    }
    

    Saga:

    export function* createItem(action) {
        ...
        const created = yield call(requestAPI.post, ITEMS_URL, newItem);
        action.callback(created)
        ...
    }
    

    The problem with this approach is that functions are not serializable and so you ideally should avoid them in your actions. Also, technically, there could be multiple sagas handling the same action and then it gets kind of confusing who should call the callback.