进行异步调用时,组件似乎不会在从同一个 thunk 分派的多个操作之间更新?
Components seem not update between multiple actions dispatched from same thunk when making async calls?
使用 react-native,当从 mapDispatchToProps 调用时,组件不会在从同一个 thunk 分派的多个动作之间更新吗?询问是因为我的用例是:尝试从 thunk 动作创建者执行异步 API 请求,但在两者之间设置和取消设置 isFetching 标志以在屏幕上显示加载微调器叠加层,如下所示:
// ComponentPresenter.js
export default class SigninScreenPresenter extends React.Component {
constructor(props) {
super(props);
}
componentDidUpdate() {
console.log(`isFetching=${this.props.isFetching}`)
}
handlePress = () => {
this.props.stateCallback()
}
render() {
return(
<SomeOtherStuff onPress={this.handlePress.bind(this)}/>
<Spinner visible={this.props.isFetching} textContent={"Loading..."} textStyle={{color: '#FFF'}} />
)
}
}
// ComponentContainer.js
const mapStateToProps = (state, ownProps) => {
// checking state changes
console.log(`mapping state to props: state.session.isFetching=${state.session.isFetching}`)
return {
isFetching: state.session.isFetching,
ownProps: ownProps
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
stateCallback: () => {
dispatch(apiRequestThunk())
}
}
}
const ComponentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ComponentPresenter)
export default ComponentContainer
// actions/index.js
.....
export function setFetchingFlag() {
return {type: SET_FETCHING}
}
export function unsetFetchingFlag() {
return {type: UNSET_FETCHING}
}
export function apiRequestThunk() {
return function (dispatch) {
dispatch(setFetchingFlag())
fetch(...making some REST request...)
.then(
(res) => {dispatch(someSuccessAction())},
(error) => {dispatch(someFailureAction())})
dispatch(unsetFetchingFlag())
}
}
.....
// reducers/index.js
.....
const sessionReducer = (state=initialState.session, action) => {
switch (action.type) {
case FLAG_API_REQUEST:
console.log('fetching API request')
return Object.assign({}, state, {isFetching: true})
case UNFLAG_API_REQUEST:
console.log('done fetching API request')
return Object.assign({}, state, {isFetching: false})
default:
return state
}
}
.....
const rootReducer = combineReducers({
session: sessionReducer,
otherStateFields: otherReducers,
.....
})
export default rootReducer
我在演示器组件中触发 handlePress() 时看到的控制台输出是
[14:49:03] fetching API request (from reducers)
[14:49:03] mapping state to props: state.session.isFetching=true (from container)
[14:49:03] done fetching API request (from reducer)
[14:49:03] mapping state to props: state.session.isFetching=false (from container)
[14:49:03] In component presenter: isFetching=false (from presenter (I would expect another one before this with isFetching=True))
并且微调器从不渲染。
注意没有输出 "In component presenter: isFetching=true"(就好像它从不从映射到状态读取,即使输出表明它正在被调用)。这使得组件似乎没有使用 mapStateToProps 值进行实际更新,直到在演示者组件中调用的 mapDispatchToCallback 函数 finished 执行之后。
这里还有什么问题吗?有更多 'best-practice' 的方法吗? thunk 和 redux 的新手,所以任何调试建议将不胜感激。
我的建议是在异步请求 resolved/rejected 后调用 dispatch(unsetFetchingFlag())
。此处您启动加载程序,然后发送 async 请求。这可以。但是,无需等待请求解析,您就可以取消设置加载程序。我相信这就是问题所在。查看此解决方案。
export function apiRequestThunk() {
return dispatch => {
// Start the loader
dispatch(setFetchingFlag());
// Send the async request
fetch(...making some REST request...)
.then(res => {
// The request has been resolved. So stop the loader.
dispatch(someSuccessAction());
dispatch(unsetFetchingFlag());
})
.catch(error => {
// An error has occurred. So
// 1. Stop the loader.
// 2. Display the error, if require
dispatch(unsetFetchingFlag());
dispatch(someFailureAction());
});
}
}
使用async/await
export function apiRequestThunk() {
return async dispatch => {
// Start the loader
dispatch(setFetchingFlag());
try {
// Send the async request
const response= await fetch(...making some REST request...);
// Once the request gets resolved, stop the loader.
// If any error occur in the request, then the error will be caught in the 'catch' block
dispatch(someSuccessAction());
dispatch(unsetFetchingFlag());
}
catch(error) {
// An error has occurred. So
// 1. Stop the loader.
// 2. Display the error, if required
dispatch(unsetFetchingFlag());
dispatch(someFailureAction());
}
}
}
也无需将 this 绑定到 onPress
,因为您已经在使用箭头函数。 <SomeOtherStuff onPress={this.handlePress}/>
使用 react-native,当从 mapDispatchToProps 调用时,组件不会在从同一个 thunk 分派的多个动作之间更新吗?询问是因为我的用例是:尝试从 thunk 动作创建者执行异步 API 请求,但在两者之间设置和取消设置 isFetching 标志以在屏幕上显示加载微调器叠加层,如下所示:
// ComponentPresenter.js
export default class SigninScreenPresenter extends React.Component {
constructor(props) {
super(props);
}
componentDidUpdate() {
console.log(`isFetching=${this.props.isFetching}`)
}
handlePress = () => {
this.props.stateCallback()
}
render() {
return(
<SomeOtherStuff onPress={this.handlePress.bind(this)}/>
<Spinner visible={this.props.isFetching} textContent={"Loading..."} textStyle={{color: '#FFF'}} />
)
}
}
// ComponentContainer.js
const mapStateToProps = (state, ownProps) => {
// checking state changes
console.log(`mapping state to props: state.session.isFetching=${state.session.isFetching}`)
return {
isFetching: state.session.isFetching,
ownProps: ownProps
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
stateCallback: () => {
dispatch(apiRequestThunk())
}
}
}
const ComponentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ComponentPresenter)
export default ComponentContainer
// actions/index.js
.....
export function setFetchingFlag() {
return {type: SET_FETCHING}
}
export function unsetFetchingFlag() {
return {type: UNSET_FETCHING}
}
export function apiRequestThunk() {
return function (dispatch) {
dispatch(setFetchingFlag())
fetch(...making some REST request...)
.then(
(res) => {dispatch(someSuccessAction())},
(error) => {dispatch(someFailureAction())})
dispatch(unsetFetchingFlag())
}
}
.....
// reducers/index.js
.....
const sessionReducer = (state=initialState.session, action) => {
switch (action.type) {
case FLAG_API_REQUEST:
console.log('fetching API request')
return Object.assign({}, state, {isFetching: true})
case UNFLAG_API_REQUEST:
console.log('done fetching API request')
return Object.assign({}, state, {isFetching: false})
default:
return state
}
}
.....
const rootReducer = combineReducers({
session: sessionReducer,
otherStateFields: otherReducers,
.....
})
export default rootReducer
我在演示器组件中触发 handlePress() 时看到的控制台输出是
[14:49:03] fetching API request (from reducers)
[14:49:03] mapping state to props: state.session.isFetching=true (from container)
[14:49:03] done fetching API request (from reducer)
[14:49:03] mapping state to props: state.session.isFetching=false (from container)
[14:49:03] In component presenter: isFetching=false (from presenter (I would expect another one before this with isFetching=True))
并且微调器从不渲染。
注意没有输出 "In component presenter: isFetching=true"(就好像它从不从映射到状态读取,即使输出表明它正在被调用)。这使得组件似乎没有使用 mapStateToProps 值进行实际更新,直到在演示者组件中调用的 mapDispatchToCallback 函数 finished 执行之后。
这里还有什么问题吗?有更多 'best-practice' 的方法吗? thunk 和 redux 的新手,所以任何调试建议将不胜感激。
我的建议是在异步请求 resolved/rejected 后调用 dispatch(unsetFetchingFlag())
。此处您启动加载程序,然后发送 async 请求。这可以。但是,无需等待请求解析,您就可以取消设置加载程序。我相信这就是问题所在。查看此解决方案。
export function apiRequestThunk() {
return dispatch => {
// Start the loader
dispatch(setFetchingFlag());
// Send the async request
fetch(...making some REST request...)
.then(res => {
// The request has been resolved. So stop the loader.
dispatch(someSuccessAction());
dispatch(unsetFetchingFlag());
})
.catch(error => {
// An error has occurred. So
// 1. Stop the loader.
// 2. Display the error, if require
dispatch(unsetFetchingFlag());
dispatch(someFailureAction());
});
}
}
使用async/await
export function apiRequestThunk() {
return async dispatch => {
// Start the loader
dispatch(setFetchingFlag());
try {
// Send the async request
const response= await fetch(...making some REST request...);
// Once the request gets resolved, stop the loader.
// If any error occur in the request, then the error will be caught in the 'catch' block
dispatch(someSuccessAction());
dispatch(unsetFetchingFlag());
}
catch(error) {
// An error has occurred. So
// 1. Stop the loader.
// 2. Display the error, if required
dispatch(unsetFetchingFlag());
dispatch(someFailureAction());
}
}
}
也无需将 this 绑定到 onPress
,因为您已经在使用箭头函数。 <SomeOtherStuff onPress={this.handlePress}/>