【问题标题】:Problems in displaying redux state changes in react componentsreact组件中显示redux状态变化的问题
【发布时间】:2023-03-25 17:05:01
【问题描述】:

对原帖的更新:我意识到问题在于 Redux 状态(WaitingRoom 组件的道具)中的更新以某种方式将组件的整个状态设置为其初始状态。因此,“showmatchingwindow”也设置为 false。最初,在分发 MATCHING_REQUEST 并将“正在加载”设置为 true 后,WaitingRoom 组件的状态仍然保持不变。然而,在 MATCHING_SUCCESS 被调度并且 "loading" 变为 false 并且 Redux 状态中的 "buddy" 和 "chatid" 被更新之后,WaitingRoom 状态中的所有字段都以某种方式重置为它们的初始值。这很奇怪,并且在我的其他 Redux 操作中没有发生。我想知道是否有人可以帮助我解释造成这种情况的原因。非常感谢您的耐心和您能提供的任何帮助!

Github 仓库链接https://github.com/liu550/salon_project(WaitingRoom 组件在 src/components/waitingroom.js 中

///以下是原帖///

我正在为我的网站构建随机匹配功能。 这里是一个简短的描述:我希望在成功匹配 2 个用户后弹出一个聊天窗口。在我的 Redux 状态中,我有(1)“正在加载”,(2)“buddy”代表我们当前用户匹配的用户的 id,以及(3)“chatid”代表存储的 firestore 文档的 id 2 个用户之间的聊天记录。在 WaitingRoom 组件中调用 startMatching Redux 操作,该组件是一个网页,除了具有匹配按钮外,还显示其他用户的个人资料。聊天窗口是MatchingWindow组件,需要一个chatid作为props来渲染2个用户之间对应的聊天记录。

我确信我的 Redux 操作可以正常工作,但是在将更新的 Redux 状态传递给我的 React 组件时出现问题。以下是我尝试过的两种方法:

方法 1:在 WaitingRoom 组件返回的模式内显示聊天窗口。如您所见,我在这里所做的只是条件渲染。然而,通过这种方法,模态以某种方式迅速出现了不到一秒钟,然后与之前用户选择其性别偏好的模态一起消失了。检查 WaitingRoom 组件的 props,我发现“buddy”和“chatid”确实更新了。不应该渲染 MatchingWindow 吗?我也试过只放 success> 而不是 MatchingWindow 组件,结果是一样的。

方法 1(等候室)

class WaitingRoom extends Component {

    state = {
      ......
    }

    showMatching = (e) => {
      e.preventDefault();
      this.setState({
        showmatching: true
      })
    }

    handleMatching = (e) => {
      e.preventDefault();
      this.props.startMatching(this.props.auth.uid, this.props.profile, this.props.profile.gender, this.state.genderpreference);
      this.setState({
        showmatchingwindow: true
      })
    }

    closeMatching = () => {
      this.setState({
        showmatching: false
      })
    }


      render() {

        const { auth, users, profile, loading, sent, buddy, chatid } = this.props;


        return (

            <div>
            <Button variant="success" onClick={this.showMatching} style={{ fontSize: "large", fontFamily: "Comic Sans MS, cursive, sans-serif"}}>Randomly find a study/work buddy instantly!</Button>
            </div>


            <Modal show={this.state.showmatching} onHide={this.closeMatching}>
              <Modal.Header closeButton></Modal.Header>
              <Modal.Body>
                <form onSubmit={this.handleMatching}>
                  Match me with:
                  <div className="form-inline">
                        <div className="radio-container">
                            <label>Male only</label><input type="radio" id="genderpreference" value="Male" onChange={this.handleChange} checked={this.state.genderpreference === "Male"} />   
                        </div>

                  ......

                  </div>

                  <button>Start matching</button>
                </form>
              </Modal.Body>
            </Modal>

            <Modal show={this.state.showmatchingwindow} onHide={this.closeMatching}>
              <Modal.Header closeButton></Modal.Header>
              <Modal.Body>
                { loading ?  <div class="loader"></div> : null}
                { buddy !== "" && loading === false ? <MatchingWindow chatid=chatid /> : null }
                { buddy === "" && loading === false ? <span>Sorry we cannot help you find a study/work buddy currently</span> : null }
              </Modal.Body>
            </Modal>

        );
      }
    }

const mapStateToProps = (state) => {
  return {
    users: state.firestore.ordered.users,
    auth: state.firebase.auth,
    profile: state.firebase.profile,
    loading: state.auth.loading,
    sent: state.auth.sent,
    buddy: state.auth.buddy,
    chatid: state.auth.chatid
  }
}

export default compose(
connect(mapStateToProps, {submitTicket, startMatching, groupMessaging}),
firestoreConnect([
{ collection: 'users' }
])
)(WaitingRoom);

方法二:创建一个单独的 MatchingBox 组件,它会根据不同的状态渲染不同的元素/组件。但是,当我采用这种方法时,在 Redux 状态更改后不会触发 componentWillReceiveProps(),因为我放置在方法中的 console.log()s 没有执行。这很奇怪,因为正如我在方法 1 中提到的,当我检查 WaitingRoom 组件的 props 时,Redux 状态确实发生了变化,这意味着我的 Redux action 和 reducer 代码应该没问题,它应该对 MatchingBox 组件工作相同也是。由于 componentWillReceiveProps() 没有被触发,我传递给 MatchingWindow 组件的 chatid 是一个空字符串,这是有问题的。我试过 componentDidUpdate() 也没有用。

方法 2(等候室)

class WaitingRoom extends Component {

      render() {

        return (

            ......All the same except for replacing the second Modal with:
            { this.state.showmatchingwindow ? <MatchingBox /> : null }

        );
      }
    }

方法 2(匹配框)

class MatchingBox extends Component {

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

    componentDidMount() {
        console.log(this.props.loading);
        console.log(this.props.chatid);
        console.log(this.props.buddy);
        this.setState({
            loading: this.props.loading,
            chatid: this.props.chatid,
            buddy: this.props.buddy
        })
    }


    componentWillReceiveProps() {
        console.log(this.props.chatid);
        console.log(this.props.buddy);
        this.setState({
            loading: this.props.loading,
            chatid: this.props.chatid,
            buddy: this.props.buddy
            })
        }

    render() {
        let box;
        if (this.props.loading === true) {
            box = <div class="loader"></div>
        }
        else {
            if (this.props.buddy !== "") {
                box = <div>
                <MatchingWindow chatid={this.state.chatid} />
                </div>    
            }
            else {
                box = <span>Sorry</span>
            }
        }
        return (
            <div>
                {box}
            </div>

        );
    }

}

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 操作和 reducer 没问题,但只是想让我的描述更容易理解。

Redux 操作

export const startMatching = (userid, userprofile, usergender, genderpreference) => (dispatch) => {

    dispatch({ type: MATCHING_REQUEST });

    ......

    dispatch(matchingConversation(userspool[number].id, doc.data(), userprofile, {time: "", from: "", content: ""}, number, userspool));      
}

export const matchingConversation = (user2id, user2profile, user1profile, message, number, userspool) => (dispatch, getState) => {

      .....
                .then(() => {
                    dispatch({ type: CHAT_SUCCESS, payload: doc.id });
                })
                .then(() => {
                    dispatch({ type: MATCHING_SUCCESS, payload: user2id });
                })
}

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

【问题讨论】:

  • WaitingRoom 组件本地状态的重置使得它很可能被卸载并重新安装。 WaitingRoom 是如何使用的?如果可以的话,与您的代码共享一个 repo 可能会更容易,或者在沙箱中创建一个可重现的示例。
  • 感谢您刚刚发布到 Github 存储库的链接的建议

标签: javascript reactjs redux react-redux


【解决方案1】:

您似乎正在使用函数来返回组件:

// main.js
const WaitingRoomPage = () => {
  return <WaitingRoom />;
};

...

<Route path='/waitingroom' component={WaitingRoomPage} />

我认为您不应该这样做,因为 Main 的每次重新渲染都会导致您的 WaitingRoom 保留 remounting (and thus re-initializing)。您的 WaitingRoom 不是功能组件,即使是,您仍然应该使用更简单的方法来声明带有 component 属性的 Route

<Route path='/waitingroom' component={WaitingRoom} />

【讨论】:

  • 嗨海市蜃楼感谢您的回复!虽然这解决了重新安装问题,但 WaitingRoom 页面上的组件状态在刷新时会丢失。例如,ChatBox 是 WaitingRoom 的子组件,ChatBox 状态的“聊天列表”设置为空数组,而 props 不是空数组。
  • 您的 ChatBox 似乎正在使用一个名为“chatshistory”的道具:&lt;ChatBox chatshistory={profile.chats_history} /&gt;,但是,该属性实际上并未在任何地方使用(第 177 行:chatlist: this.props.profile.chats_history)。您的状态 chatlist 实际上来自 redux/firebase 状态:profile: state.firebase.profile -> chatlist: this.props.profile.chats_history
  • 感谢您的回复,问题已解决!
【解决方案2】:

首先,我认为您不需要在这里使用任何内部状态。当您知道多个组件必须共享相同的变量并且它们不是分层的时,只需使用存储并使用应用程序状态。

其次,我认为在您的 reducer 中,您可能指向相同的初始状态并直接对其进行变异(违反 redux 规则)。简而言之,由于浅拷贝,您的状态可能指向相同的旧初始状态对象,因此您的反应组件没有检测到更改。

在返回新状态之前,请尝试在您的 redux reducer 中创建初始状态的深层副本(而不是浅层副本)。这些链接讨论了这些概念:

  1. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
  2. React component not updating when store state has changed

最后,你的好友初始状态不应该是空数组而不是字符串吗?

【讨论】:

    猜你喜欢
    • 2019-02-01
    • 2017-08-03
    • 2016-10-02
    • 2019-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-05
    相关资源
    最近更新 更多