在 React 样板容器中获取初始数据的正确方法
Proper Way in Fetching Initial Data in React Boilerplate Container
我是 React Boilerplate 的新手,正在使用容器和传奇来获取页面的初始数据。我在实现这个的时候遇到了两个问题。
UsersPage\index.js
// imported all sagas, etc
export function UsersPage({
dispatch,
usersPage,
onGetUsers,
onChangeSearch,
onSetActive,
}) {
useInjectReducer({ key: 'usersPage', reducer });
useInjectSaga({ key: 'usersPage', saga });
onGetUsers();
return (...);
}
UsersPage.propTypes = {
dispatch: PropTypes.func.isRequired,
usersPage: PropTypes.object,
onGetUsers: PropTypes.func,
onChangeSearch: PropTypes.func,
onSetActive: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
usersPage: makeSelectUsersPage(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
onGetUsers: () => dispatch(getUsers()),
onChangeSearch: evt => dispatch(changeSearch(evt.target.value)),
onSetActive: active => dispatch(setActive(active)),
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(withConnect)(UsersPage);
UsersPage\actions.js
...
export function getUsers() {
console.log('action getUsers');
return {
type: constants.GET_USERS,
};
}
...
UsersPage\saga.js
export function* getUsers() {
console.log('saga getUsers');
try {
const response = yield call(api.users.getAll);
yield put(
getUsersSuccess({
users: response.data.data,
}),
);
} catch (err) {
const error = err.response.data.message;
yield put(getUsersFail(error));
}
}
export default function* usersPageSaga() {
yield takeLatest(constants.GET_USERS, getUsers);
}
当我第一次加载页面时,控制台日志显示如下:
action getUsers
saga watcher
但是,当我导航到其他页面,然后转到此页面时:
action getUsers
saga worker getUsers
问题 1
似乎第一次加载时,saga watcher 的调用晚于操作 (getUsers)。因此,它在第一次加载时失败,但在随后的加载中,saga worker 被调用得很好。我如何确保 saga watcher 在操作之前首先被调用?
问题 2
因为操作 (getUsers) 在渲染内部,所以每次有更新时都会调用它。因此,当 getUsers return 并将新数据应用到 reducer 时,将再次调用渲染并再次调用操作 (getUsers),从而导致无限循环。我们应该做什么或将初始数据实际加载到容器中的正确方法是什么?
要解决第一个问题,请尝试使用 takeEvery
而不是 takeLatest
:
export default function* usersPageSaga() {
yield takeEvery(constants.GET_USERS, getUsers);
}
关于第二个问题,可以用useEffect
hook加载初始数据:
useEffect(()=> {
// load the data
}, [])
我是 React Boilerplate 的新手,正在使用容器和传奇来获取页面的初始数据。我在实现这个的时候遇到了两个问题。
UsersPage\index.js
// imported all sagas, etc
export function UsersPage({
dispatch,
usersPage,
onGetUsers,
onChangeSearch,
onSetActive,
}) {
useInjectReducer({ key: 'usersPage', reducer });
useInjectSaga({ key: 'usersPage', saga });
onGetUsers();
return (...);
}
UsersPage.propTypes = {
dispatch: PropTypes.func.isRequired,
usersPage: PropTypes.object,
onGetUsers: PropTypes.func,
onChangeSearch: PropTypes.func,
onSetActive: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
usersPage: makeSelectUsersPage(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
onGetUsers: () => dispatch(getUsers()),
onChangeSearch: evt => dispatch(changeSearch(evt.target.value)),
onSetActive: active => dispatch(setActive(active)),
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(withConnect)(UsersPage);
UsersPage\actions.js
...
export function getUsers() {
console.log('action getUsers');
return {
type: constants.GET_USERS,
};
}
...
UsersPage\saga.js
export function* getUsers() {
console.log('saga getUsers');
try {
const response = yield call(api.users.getAll);
yield put(
getUsersSuccess({
users: response.data.data,
}),
);
} catch (err) {
const error = err.response.data.message;
yield put(getUsersFail(error));
}
}
export default function* usersPageSaga() {
yield takeLatest(constants.GET_USERS, getUsers);
}
当我第一次加载页面时,控制台日志显示如下:
action getUsers
saga watcher
但是,当我导航到其他页面,然后转到此页面时:
action getUsers
saga worker getUsers
问题 1
似乎第一次加载时,saga watcher 的调用晚于操作 (getUsers)。因此,它在第一次加载时失败,但在随后的加载中,saga worker 被调用得很好。我如何确保 saga watcher 在操作之前首先被调用?
问题 2
因为操作 (getUsers) 在渲染内部,所以每次有更新时都会调用它。因此,当 getUsers return 并将新数据应用到 reducer 时,将再次调用渲染并再次调用操作 (getUsers),从而导致无限循环。我们应该做什么或将初始数据实际加载到容器中的正确方法是什么?
要解决第一个问题,请尝试使用 takeEvery
而不是 takeLatest
:
export default function* usersPageSaga() {
yield takeEvery(constants.GET_USERS, getUsers);
}
关于第二个问题,可以用useEffect
hook加载初始数据:
useEffect(()=> {
// load the data
}, [])