Reputation: 341
I'm a little new to react, redux, and sagas, but I'm getting the hang of things.
I have a component (Results.jsx
) that displays results of a particular real-world event, through a saga calling an external API:
componentDidMount() {
if (this.props.thing_id) {
this.props.getResults(this.props.thing_id);
}
}
...
const mapStateToProps = (state) => {
return {
prop1: state.apiReducer.thing_results.data1,
prop2: state.apiReducer.thing_results.data2,
fetching: state.apiReducer.fetching,
error: state.apiReducer.error,
};
};
const mapDispatchToProps = (dispatch) => {
return {
getResults: (thing_id) => dispatch({type: "RESULTS_DATA_REFRESH", payload: thing_id})
};
};
This all works great. Until... Well, I'm using a tabbed interface that lets me dynamically add a bunch of additional instances of Results.jsx
so I can see a bunch of different results sets all on the same screen.
The problem is that when a new instance of the Results.jsx
component loads, and gets data from the RESULTS_DATA_REFRESH
dispatch, all of the instances of the Results.jsx
component update with the data that comes back. They all show the same data.
For the life of me, I can't figure out how to have a particular instance of a component only listen to results from what it itself dispatched. I thought that's the way sagas were supposed to work?
Any help is appreciated!
Edits/Answers:
Reducer function is pretty textbook, looks like:
const initialState = {
fetching: false,
error: null,
thing_results: {
data1: null,
data2: null,
},
};
export default (state = initialState, action) => {
switch (action.type) {
//...
case "RESULTS_DATA_REFRESH":
return {...state, fetching: true};
case "RESULTS_DATA_SUCCESS":
return {...state, fetching: false, thing_results: action.results.data, error: null};
case "RESULTS_DATA_FAILURE":
return {...state, fetching: false, thing_results: null, error: action.error};
default:
return state;
}
};
Upvotes: 0
Views: 111
Reputation: 5402
Sagas are nothing but a middleware to offload your async tasks and store writes out of the View layer. Ultimately the prop that comes to your component depends on how you store it. Specifically in this case if prop1
and prop2
are picked up from the same place in the store, it'll come as the same value in all instances of Results
.
If you require different data for different instances, section it based on some unique id mapped to the instance. You reducer would look like :
const apiReducer = (state = {}, action) => {
switch (action.type) {
case "RESULTS_DATA_REFRESH":
return {
...state,
[action.payload]: { data: null, fetching: true }
};
case "RESULTS_DATA_SUCCESS":
return {
...state,
/** You should be getting back the id from the api response.
* Else append the id in the success action from your api saga.
*/
[action.payload.id]: { data: action.results.data, fetching: false }
};
case "RESULTS_DATA_FAILURE":
return {
...state,
[action.payload.id]: {
data: null,
fetching: false,
error: action.error
}
};
default:
return state;
}
};
/** Other reducers */
const otherReducerA = function() {};
const otherReducerB = function() {};
export default combineReducers({ apiReducer, otherReducerA, otherReducerB });
And access it like :
const mapStateToProps = state => {
return {
data: state.apiReducer
};
};
function Results({ data, thing_id }) {
return <div>{data[thing_id].data}</div>;
}
Upvotes: 2