Reputation: 2588
Fairly simple use case: I have actions/events that will cause an ajax request to be executed and then update a list.
The problem is I'm not sure how (specifically, where to kick off a request for a new list when the page is changed.
redux store
const defaultStore = {
list: [],
page: 1
};
Wrapper Component
const wrapper = props => (
<div>
<List {...props}> {/* This list should have page controls */}
<PageControls {...props} />
</List>
<List /> {/* This list should not have page controls */}
</div>
);
List component
const List = props => (
<div>
{props.children} {/* render page controls if present */}
{props.items.map((item, k) => <div key={k}>item</div>
</div>
);
Pager Controls component
const PageControls = props => (
<div>
<span onClick={props.changePage(-1)}>Backward</span>
<span onClick={props.changePage(1)}>Forward</span>
</div>
);
actionCreator
function changePage(delta) {
return {
type: 'CHANGE_PAGE',
delta
};
}
// utilizes react-thunk middleware
function getList(page = 1) {
return dispatch =>
axios.get(`/path/to/api?page=${page}`)
.then(res => dispatch(updateList(res.data));
}
function updateList(newList) {
return {
type: 'UPDATE_LIST',
newList
};
}
reducer
function reducer(state = {}, action) {
switch(action.type) {
case 'CHANGE_PAGE':
return {...state, page: state.page + action.delta};
case 'UPDATE_LIST':
return {...state, list: action.newList};
default:
return state;
}
}
At this point I could do a couple of things -- I could make every actionCreator that should trigger a list update dispatch that action:
function changePage(delta) {
return dispatch => {
dispatch({
type: 'CHANGE_PAGE',
delta
});
return dispatch(getList(store.getState() + delta));
}
}
But this seems messy. Now not only do I have to get my store
but I also have to turn every actionCreator that affects the list into a thunk.
The only other thing I can think of is to have my <List>
component use store.subscribe
somewhere to watch for changes to page and then kick off another getList
action, but this also seems like I'm moving the understanding of what does and does not trigger state changes out of Redux and into my React components.
Any ideas?
Upvotes: 1
Views: 713
Reputation: 508
Well, maybe you should change your approach. I don't see a reason to make two actions for changing page and retrieving the list. You can just dispatch getPage()
action on button click, passing next page number. This should retrieve list of items and refresh your page.
In your store you should keep track on current page, so each time page refreshes the value of getPage()
argument will also update.
For example (assuming that current page is not retrieved from API):
function getPage(page = 1) {
return dispatch =>
axios.get(`/path/to/api?page=${page}`)
.then(res => dispatch(updatePage(res.data, page));
}
function updatePage(newList, currentPage) {
return {
type: 'UPDATE_PAGE',
newList,
currentPage,
};
}
and connect required components to the store, in your case it would be List
and PageControls
components.
const PageControls = props => (
<div>
<span onClick={props.getPage(props.currentPage - 1)}>Backward</span>
<span onClick={props.getPage(props.currentPage + 1)}>Forward</span>
</div>
);
This will allow you to maintain simple and clean code. Also you can trigger it from multiple, not related components.
Upvotes: 1