在 React/Redux 中放置链接操作的位置
Where to put chained actions in React/Redux
相当简单的用例:我有 actions/events 会导致执行 ajax 请求,然后更新列表。
问题是我不确定如何(具体来说,在哪里在页面更改时启动对新列表的请求。
redux 商店
const defaultStore = {
list: [],
page: 1
};
包装组件
const wrapper = props => (
<div>
<List {...props}> {/* This list should have page controls */}
<PageControls {...props} />
</List>
<List /> {/* This list should not have page controls */}
</div>
);
列表组件
const List = props => (
<div>
{props.children} {/* render page controls if present */}
{props.items.map((item, k) => <div key={k}>item</div>
</div>
);
分页器控件组件
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
};
}
减速器
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;
}
}
此时我可以做几件事 -- 我可以让每个应该触发列表更新的 actionCreator 分派该操作:
function changePage(delta) {
return dispatch => {
dispatch({
type: 'CHANGE_PAGE',
delta
});
return dispatch(getList(store.getState() + delta));
}
}
但这看起来很乱。现在我不仅要得到我的 store
,而且我还必须将影响列表的每个 actionCreator 变成一个 thunk。
我唯一能想到的另一件事是让我的 <List>
组件在某处使用 store.subscribe
来观察页面的变化,然后启动另一个 getList
动作,但是这个似乎我正在将对什么会触发和不会触发状态更改的理解从 Redux 转移到我的 React 组件中。
有什么想法吗?
嗯,也许你应该改变你的方法。我看不出有理由对更改页面和检索列表进行两项操作。您可以在单击按钮时发送 getPage()
操作,传递下一页编号。这应该会检索项目列表并刷新您的页面。
在您的商店中,您应该跟踪当前页面,因此每次页面刷新时 getPage()
参数的值也会更新。
例如(假设当前页面不是从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,
};
}
并将所需的组件连接到商店,在您的情况下它将是 List
和 PageControls
组件。
const PageControls = props => (
<div>
<span onClick={props.getPage(props.currentPage - 1)}>Backward</span>
<span onClick={props.getPage(props.currentPage + 1)}>Forward</span>
</div>
);
这将使您能够维护简单干净的代码。您也可以从多个不相关的组件触发它。
相当简单的用例:我有 actions/events 会导致执行 ajax 请求,然后更新列表。
问题是我不确定如何(具体来说,在哪里在页面更改时启动对新列表的请求。
redux 商店
const defaultStore = {
list: [],
page: 1
};
包装组件
const wrapper = props => (
<div>
<List {...props}> {/* This list should have page controls */}
<PageControls {...props} />
</List>
<List /> {/* This list should not have page controls */}
</div>
);
列表组件
const List = props => (
<div>
{props.children} {/* render page controls if present */}
{props.items.map((item, k) => <div key={k}>item</div>
</div>
);
分页器控件组件
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
};
}
减速器
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;
}
}
此时我可以做几件事 -- 我可以让每个应该触发列表更新的 actionCreator 分派该操作:
function changePage(delta) {
return dispatch => {
dispatch({
type: 'CHANGE_PAGE',
delta
});
return dispatch(getList(store.getState() + delta));
}
}
但这看起来很乱。现在我不仅要得到我的 store
,而且我还必须将影响列表的每个 actionCreator 变成一个 thunk。
我唯一能想到的另一件事是让我的 <List>
组件在某处使用 store.subscribe
来观察页面的变化,然后启动另一个 getList
动作,但是这个似乎我正在将对什么会触发和不会触发状态更改的理解从 Redux 转移到我的 React 组件中。
有什么想法吗?
嗯,也许你应该改变你的方法。我看不出有理由对更改页面和检索列表进行两项操作。您可以在单击按钮时发送 getPage()
操作,传递下一页编号。这应该会检索项目列表并刷新您的页面。
在您的商店中,您应该跟踪当前页面,因此每次页面刷新时 getPage()
参数的值也会更新。
例如(假设当前页面不是从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,
};
}
并将所需的组件连接到商店,在您的情况下它将是 List
和 PageControls
组件。
const PageControls = props => (
<div>
<span onClick={props.getPage(props.currentPage - 1)}>Backward</span>
<span onClick={props.getPage(props.currentPage + 1)}>Forward</span>
</div>
);
这将使您能够维护简单干净的代码。您也可以从多个不相关的组件触发它。