Reputation: 14210
I have a parent react component containing 3 children:
<ChildComponent category="foo" />
<ChildComponent category="bar" />
<ChildComponent category="baz" />
The child component calls an api depending on the prop category value:
http://example.com/listings.json?category=foo
In my action the data is returned as expected. However, when the child component renders the data. The last child baz is overwriting its value in foo and bar as well.
A solution to this problem seems to be given here. But I would like this to be dynamic and only depend on the category prop. Is this not possible to do in Redux?
My child component looks like this:
class TweetColumn extends Component {
componentDidMount() {
this.props.fetchTweets(this.props.column)
}
render() {
const { tweets, column } = this.props
if (tweets.length === 0) { return null }
const tweetItems = tweets[column].map(tweet => (
<div key={ tweet.id }>
{ tweetItems.name }
</div>
)
return (
<div className="box-content">
{ tweetItems }
</div>
)
}
}
TweetColumn.propTypes = {
fetchTweets: PropTypes.func.isRequired,
tweets: PropTypes.array.isRequired
}
const mapStateToProps = state => ({
tweets: state.tweets.items
})
export default connect(mapStateToProps, { fetchTweets })( TweetColumn )
reducers:
export default function tweetReducer(state = initialState, action) {
switch (action.type) {
case FETCH_TWEETS_SUCCESS:
return {
...state,
[action.data[0].user.screen_name]: action.data
}
default:
return state;
}
}
export default combineReducers({
tweets: tweetReducer,
})
action:
export const fetchTweets = (column) => dispatch => {
dispatch({ type: FETCH_TWEETS_START })
const url = `${ TWITTER_API }/statuses/user_timeline.json?count=30&screen_name=${ column }`
return axios.get(url)
.then(response => dispatch({
type: FETCH_TWEETS_SUCCESS,
data: response.data
}))
.then(response => console.log(response.data))
.catch(e => dispatch({type: FETCH_TWEETS_FAIL}))
}
Upvotes: 0
Views: 288
Reputation: 1545
You are making an api call every time TweetColumn
is mounted. If you have multiple TweetColumn
components and each one makes an api call, then whichever one's response is last to arrive is going to set the value of state.tweets.items
. That's because you are dispatching the same action FETCH_TWEETS_SUCCESS
every time (the last one overrides the previous one). To solve that issue, assuming the response has a category
(foo, bar, baz), I would write the reducer in the following way:
export default function tweetReducer(state = initialState, action) {
switch (action.type) {
case FETCH_TWEETS_SUCCESS:
return {
...state,
[action.data.category]: action.data
}
default:
return state;
}
}
You can then do the following in your TweetColumn
component:
class TweetColumn extends Component {
componentDidMount() {
this.props.fetchTweets(this.props.column)
}
render() {
const { column } = this.props;
const tweetItems = this.props.tweets[column].map(tweet => (
<div key={ tweet.id }>
{ tweet.name }
</div>
)
return (
<div className="box-content">
{ tweetItems }
</div>
)
}
}
const mapStateToProps = state => ({
tweets: state.tweets
})
const mapDispatchToProps = dispatch => ({
fetchTweets: column => dispatch(fetchTweets(column))
})
export default connect(
mapStateToProps,
mapDispatchToProps,
)( TweetColumn )
You will have to do some validation to make sure tweets[column]
exists, but you get the idea.
Upvotes: 2