简体   繁体   English

显示反应组件中 redux state 变化的问题

[英]Problems in displaying redux state changes in react components

Update to the original post: I have realized that the issue is that the updates in the Redux state (props to the WaitingRoom component) somehow set the entire state of the component to its initial state. Update to the original post: I have realized that the issue is that the updates in the Redux state (props to the WaitingRoom component) somehow set the entire state of the component to its initial state. Thus, "showmatchingwindow" was set to false as well.因此,“showmatchingwindow”也设置为 false。 Initially, after MATCHING_REQUEST was dispatched and "loading" was set to true, the state of WaitingRoom component still remained unchanged.最初,在分发 MATCHING_REQUEST 并将“加载”设置为 true 后,WaitingRoom 组件的 state 仍然保持不变。 However, after MATCHING_SUCCESS was dispatched and "loading" turned false and "buddy" and "chatid" in the Redux state got updated, all the fields in the state of WaitingRoom were somehow reset to their initial values.但是,在发送 MATCHING_SUCCESS 并且“加载”变为假并且 Redux state 中的“伙伴”和“chatid”得到更新后,Z9ED39E2EA931585A69 中的所有字段都以某种方式重置为 573 其初始值。 This is weird and did not happen with my other Redux actions.这很奇怪,并且在我的其他 Redux 操作中没有发生。 I am wondering whether anyone can help me explain what caused this.我想知道是否有人可以帮助我解释造成这种情况的原因。 Really appreciate your patience and any help you can offer!非常感谢您的耐心和您可以提供的任何帮助!

Link to the Github repo : https://github.com/liu550/salon_project (WaitingRoom component is in src/components/waitingroom.js链接到 Github 存储库https://github.com/liu550/salon_project (WaitingRoom 组件位于 src/components/waitingroom.js

///Below is the original post/// ///下面是原文///

I am building a random matching feature for my website.我正在为我的网站构建随机匹配功能。 Here is a brief description : I want a chat window to pop up once 2 users are successfully matched up.这里有一个简短的描述:我想要一个聊天 window 一旦成功匹配 2 个用户就会弹出。 Inside my Redux state, I have (1) "loading," (2) "buddy" representing the id of the user that our current user is matched with, and (3) "chatid" representing the id of the firestore document that stores the chat history between 2 users.在我的 Redux state 中,我有(1)“正在加载”,(2)“伙伴”代表我们当前用户匹配的用户的 ID,以及(3)“chatid”代表存储的 Firestore 文档的 ID 2 个用户之间的聊天记录。 The startMatching Redux action is called in the WaitingRoom component which is a web page that displays other users' profiles in addition to having the matching button.在 WaitingRoom 组件中调用 startMatching Redux 操作,这是一个 web 页面,除了具有匹配按钮外,还显示其他用户的配置文件。 The chat window is the MatchingWindow component, which needs a chatid as its props to render the corresponding chat history between 2 users.聊天 window 是 MatchingWindow 组件,它需要一个 chatid 作为它的 props 来渲染两个用户之间的对应聊天记录。

I am sure that my Redux actions work fine but got problems when passing the updated Redux state to my React components.我确信我的 Redux 操作可以正常工作,但是在将更新的 Redux state 传递给我的 React 组件时出现问题。 Below are 2 approaches that I tried:以下是我尝试过的两种方法:

Approach 1 : Display the chat window inside a modal returned by the WaitingRoom component.方法 1 :在 WaitingRoom 组件返回的模式中显示聊天 window。 As you can see, what I am doing here is just conditional rendering.如您所见,我在这里所做的只是条件渲染。 However, with this approach, the modal somehow quickly showed up for less than a second and then disappeared along with the previous modal where users select their gender preferences.然而,通过这种方法,模态以某种方式迅速出现了不到一秒钟,然后与之前的模态一起消失,用户 select 是他们的性别偏好。 Checking the props to the WaitingRoom component, I found that "buddy" and "chatid" were indeed updated.检查 WaitingRoom 组件的 props,我发现“buddy”和“chatid”确实更新了。 Shouldn't MatchingWindow be rendered?不应该渲染 MatchingWindow 吗? I also tried just putting success> instead of the MatchingWindow component and it was the same outcome.我也尝试只使用 success> 而不是 MatchingWindow 组件,结果相同。

Approach 1 (WaitingRoom)方法一(等候室)

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

Approach 2 : Create a separate MatchingBox component, which will render different elements/components based on different states.方法二:创建一个单独的 MatchingBox 组件,它会根据不同的状态渲染不同的元素/组件。 However, when I went with this approach, componentWillReceiveProps() was not fired after the Redux state changes, as the console.log()s I placed inside the method were not executed.但是,当我采用这种方法时,在 Redux state 更改后没有触发 componentWillReceiveProps(),因为我放置在方法中的 console.log()s 没有执行。 It was weird because, as I mentioned in approach 1, the Redux state was indeed changed when I checked the props to the WaitingRoom component, which means that my Redux action and reducer code should be fine and it should work the same for the MatchingBox component as well. It was weird because, as I mentioned in approach 1, the Redux state was indeed changed when I checked the props to the WaitingRoom component, which means that my Redux action and reducer code should be fine and it should work the same for the MatchingBox component以及。 As a result of componentWillReceiveProps() not being fired, the chatid I passed to the MatchingWindow component was an empty string, which is problematic.由于 componentWillReceiveProps() 没有被触发,我传递给 MatchingWindow 组件的 chatid 是一个空字符串,这是有问题的。 I tried componentDidUpdate() and it did not work either.我尝试了 componentDidUpdate() ,它也没有工作。

Approach 2 (WaitingRoom)方法二(等候室)

class WaitingRoom extends Component {

      render() {

        return (

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

        );
      }
    }

Approach 2 (MatchingBox)方法 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);

As stated above, I am pretty sure my redux actions and reducer are fine but just want to make my description more understandable.如上所述,我很确定我的 redux 动作和减速器很好,但只是想让我的描述更容易理解。

Redux actions 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 reducer 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;
    }
}

Firstly, I don't think you need to use any internal state here.首先,我认为您不需要在这里使用任何内部 state。 When you know multiple components have to share same variables, and they are not hierarchial, just use a store and use application state.当您知道多个组件必须共享相同的变量并且它们不是分层的时,只需使用存储并使用应用程序 state。

Secondly, I think in your reducer, you might be pointing to the same initial state and directly mutating it (against redux rules).其次,我认为在您的减速器中,您可能指向相同的初始 state 并直接对其进行变异(违反 redux 规则)。 In short, due to shallow copy, your state might be pointing to the same old initial state object and thus your react component is not detecting a change.简而言之,由于浅拷贝,您的 state 可能指向相同的旧初始 state object,因此您的反应组件未检测到更改。

Try creating a deep copy (not shallow copy) of your initial state in your redux reducer before you return a new state.在返回新的 state 之前,尝试在 redux 减速器中创建初始 state 的深层副本(不是浅层副本)。 These links talk about these concepts:这些链接讨论了这些概念:

  1. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
  2. React component not updating when store state has changed 存储 state 已更改时反应组件未更新

Lastly, should not your buddy in the initial state be an empty array instead of a string?最后,您在初始 state 中的伙伴不应该是一个空数组而不是一个字符串吗?

You seem to be using a function to return the component:您似乎正在使用 function 来返回组件:

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

...

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

I don't think you should be doing that because every re-render of Main will cause your WaitingRoom to keep remounting (and thus re-initializing) .我认为您不应该这样做,因为 Main 的每次重新渲染都会导致您的WaitingRoom继续 重新安装(并因此重新初始化) Your WaitingRoom is not a functional component, and even if it was, you should still just use the simpler way of declaring a Route with a component attribute:您的WaitingRoom不是功能组件,即使它是,您仍然应该使用更简单的方法来声明具有component属性的Route

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM