进行异步调用时,组件似乎在同一重击派发的多个操作之间没有更新吗?

时间:2018-07-16 02:51:51

标签: react-native redux-thunk

使用react-native,在从mapDispatchToProps调用时,组件是否在同一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))

并且Spinner永远不会渲染。

请注意,没有输出“在组件演示者中:isFetching = true”(就好像它从未从映射到状态读取一样,即使输出表明正在调用它也是如此)。这使得该组件似乎没有使用mapStateToProps值进行手动更新,直到在Presenter组件中调用的mapDispatchToCallback函数完成执行之后。

这里还有其他问题吗?有没有更多的“最佳实践”方法来做到这一点? thunk和redux的新功能,因此不胜感激任何调试建议。

1 个答案:

答案 0 :(得分:1)

我的建议是一旦异步请求已解决/拒绝,就致电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());
            });
    }
}

使用异步/等待

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}/>