【问题标题】:Redux dispatching actions sequentiallyRedux 顺序调度动作
【发布时间】:2020-09-10 17:55:00
【问题描述】:

我正在为我的网站构建随机匹配功能。在 startMatching Redux 动作结束时,如果匹配成功,我想在调度 MATCHING_SUCCESS 之前调度另一个名为 startConversation 的动作。 startConversation 操作用于创建或更新 2 个用户之间的聊天历史记录,并将在最后发送存储聊天历史记录作为有效负载的 firestore 文档的 id。由于 (1) startConversation 在 MATCHING_SUCCESS 之前调度并且 (2) startConversation 调度聊天历史文档的 id,我可以在我的 Redux 状态中创建一个名为 chatid 的字段,以便在匹配成功完成后访问文档的 id 并使用它生成类似聊天窗口的东西。

我一直在用一个应该成功完成匹配的案例测试代码。我遇到的问题是第一次调度(startConversation)在第二次调度(MATCHING_SUCCESS)完成后完成。之所以知道这一点,是因为我把console.log放在了MatchingBox组件的componentDidUpdate方法中,并且放在startConversation动作的最后,而前者的console.log比后者更早执行,也就是说MATCHING_SUCCESS在startConversation之前已经完成了调度这会导致 Redux 状态发生变化,从而影响组件。实际上,MatchingBox 组件状态中的 buddy 和 loading 字段已更新,而 chatid 仍为空字符串。我不希望这种情况发生,因为如您所见,我需要将 chatid 进一步向下传递给 MatchingWindow 组件。

这让我很困惑:毕竟,Redux 不是同步的吗?此外,我尝试使用“then”链接 2 个调度,并且 startConversation 仍然在 MATCHING_SUCCESS 之后完成调度。我想知道如何解决这个问题。非常感谢您的耐心等待!

Redux 操作


export const startConversation = (user2id, user2profile, user1profile, message) => (dispatch, getState) => {

  ......

  console.log(chatid);
  dispatch({ type: CHAT_SUCCESS, payload: chatid });

}


export const startMatching = (userid, userprofile, usergender, genderpreference) => (dispatch) => {
    dispatch({ type: MATCHING_REQUEST });

  ...Matching algorithm...

  //Dispatches                      
 firebase.firestore().collection("users").doc(userspool[number].id).get()
     .then((doc) => {
       dispatch(startConversation(userspool[number].id, doc.data(), userprofile, {time: "", from: "", content: ""}));      
       dispatch({ type: MATCHING_SUCCESS, payload: doc.data() });
      })

  if (buddy === "") {
      dispatch({ type: MATCHING_FAIL, payload: [] });
    }

  console.log(buddy);      
}

Redux 减速器

const initialState = {

    ......

    loading: false,
    chatid: "",
    buddy: [],
}

const authReducer = (state = initialState, action) => {
    const {type, payload} = action;

    switch(type) {

        ......

        case CHAT_SUCCESS:
            return {
                ...state,
                chatid: action.payload
            }
        case MATCHING_REQUEST:
            return {
                ...state,
                loading: true
            }
        case MATCHING_SUCCESS:
            return {
                ...state,
                buddy: action.payload,
                loading: false
            }
        case MATCHING_FAIL:
            return {
                ...state,
                buddy: action.payload,
                loading: false
            }

        default: return state;
    }
}

MatchingBox 组件

class MatchingBox extends Component {

    state = {
        show: true,
        loading: false,
        chatid: "",
        buddy: []
    }

    componentDidMount() {
        this.setState({
            loading: this.props.loading,
        })
    }


    componentDidUpdate(prevprops) {
        console.log(this.props.chatid);
        console.log(this.props.buddy.first_name);
        if (prevprops.loading !== this.props.loading) {
            this.setState({
                loading: this.props.loading,
                chatid: this.props.chatid,
                buddy: this.props.buddy
            })
        }
    }



    render() {

        let box;
        if (this.props.loading === true) {
            box = <span>Loading...</span>
        }
        else {

            if (this.props.buddy.length !== 0) {
                box = <div>
                <div className="form-inline">
                    <img src={this.state.buddy.image}></img>
                    <img src={this.props.profile.image}></img>
                </div>
                <MatchingWindow chatid={this.state.chatid} />
                </div>
            }

            else {
                box = <span>Sorry we cannot help you find a study/work buddy currently</span>
            }
        }
        return (
            <Modal show={this.state.show}>
            <Modal.Header closeButton></Modal.Header>
            <Modal.Body>
              {box}
            </Modal.Body>
          </Modal>
        );
    }

}

const mapStateToProps = (state) => {
    return {
        auth: state.firebase.auth,
        loading: state.auth.loading,
        chatid: state.auth.chatid,
        buddy: state.auth.buddy,
        profile: state.firebase.profile
    };
}

export default connect(mapStateToProps)(MatchingBox);

【问题讨论】:

    标签: redux react-redux redux-thunk react-redux-firebase redux-firestore


    【解决方案1】:

    看起来您正在使用 redux thunk,正如 docs 所说:

    Redux Thunk 中间件允许您编写返回的动作创建器 一个函数而不是一个动作。 thunk 可用于延迟 分派一个动作,或仅在某个条件满足时才分派 遇见了。内部函数接收 store 方法调度和 getState 作为参数。

    你的 action reducer startConversation 做了一些异步操作吗?这样,redux thunk 将保持该调度执行直到其解决,但不会阻止执行以下代码。

    下一行MATCHING_SUCCESS 立即执行,它不等待未决的前一个被解决。同样,MATCHING_FAIL 不在承诺范围内,可能在所有调度之前触发(MATCHING_REQUEST 除外)。

    由于startConversation 是异步的,并且您需要在其解析后执行这些调度,您应该考虑在startConversation 调用MATCHING_SUCCESSMATCHING_FAIL

    【讨论】:

    • 感谢您的帮助!我仍然不明白为什么我们不能 et MATCHING_SUCCESS 等待 startConversation 通过使用 then 来解决:firebase.firestore().collection("users").doc(...).get().then((doc) = > { dispatch(startConversation(...)); return doc; }).then((doc) => { dispatch({ type: MATCHING_SUCCESS, payload: doc.data() }) });
    猜你喜欢
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    相关资源
    最近更新 更多