react组件中显示redux状态变化的问题 [英] Problems in displaying redux state changes in react components

查看:57
本文介绍了react组件中显示redux状态变化的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Github 存储库链接:https://github.com/liu550/沙龙项目(WaitingRoom 组件在 src/components/waitingroom.js 中

///以下为原帖///

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

我确信我的 Redux 操作运行良好,但在将更新的 Redux 状态传递给我的 React 组件时出现问题.以下是我尝试过的两种方法:

方法 1:在 WaitingRoom 组件返回的模式中显示聊天窗口.如您所见,我在这里所做的只是条件渲染.然而,通过这种方法,模态不知何故迅速出现了不到一秒钟,然后随着用户选择他们的性别偏好的先前模态而消失.查看WaitingRoom组件的props,发现buddy"和chatid"确实更新了.不应该渲染 MatchingWindow 吗?我也试过只用 success> 而不是 MatchingWindow 组件,结果是一样的.

方法一(等候室)

class WaitingRoom 扩展组件 {状态 = {......}showMatching = (e) =>{e.preventDefault();this.setState({匹配:真实})}handleMatching = (e) =>{e.preventDefault();this.props.startMatching(this.props.auth.uid, this.props.profile, this.props.profile.gender, this.state.genderpreference);this.setState({显示匹配窗口:true})}closeMatching = () =>{this.setState({匹配:假})}使成为() {const { auth, users, profile, loading, sent, buddy, chatid } = this.props;返回 (<div><Button variant="success" onClick={this.showMatching} style={{ fontSize: "large", fontFamily: "Comic Sans MS, cursive, sans-serif"}}>随机找一个学习/工作的小伙伴瞬间!</按钮>

<Modal show={this.state.showmatching} onHide={this.closeMatching}><Modal.Header closeButton></Modal.Header><模态体><form onSubmit={this.handleMatching}>匹配我:<div className="form-inline"><div className="radio-container"><label>仅限男性</label><input type="radio" id="genderpreference" value="Male" onChange={this.handleChange} 选中={this.state.genderpreference === "Male"}/>

......

<button>开始匹配</button></表单></Modal.Body></模态><模态显示={this.state.showmatchingwindow} onHide={this.closeMatching}><Modal.Header closeButton></Modal.Header><模态体>{加载?<div class="loader"></div>: 空值}{ 哥们!== "" &&加载 === 假?<MatchingWindow chatid=chatid/>: 空值 }{ 哥们 === "" &&加载 === 假?<span>抱歉,我们目前无法帮您找到学习/工作伙伴</span>: 空值 }</Modal.Body></模态>);}}const mapStateToProps = (状态) =>{返回 {用户:state.firestore.ordered.users,身份验证:state.firebase.auth,配置文件:state.firebase.profile,加载:state.auth.loading,发送:state.auth.sent,好友:state.auth.buddy,聊天ID:state.auth.chatid}}导出默认撰写(连接(mapStateToProps,{submitTicket,startMatching,groupMessaging}),firestoreConnect([{集合:'用户'}]))(等候室);

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

方法 2(等候室)

class WaitingRoom 扩展组件 {使成为() {返回 (......除了将第二个 Modal 替换为:{ this.state.showmatchingwindow ?<MatchingBox/>: 空值 });}}

方法 2(MatchingBox)

class MatchingBox 扩展组件 {状态 = {加载:真实,聊天:"",伙伴: []}componentDidMount() {console.log(this.props.loading);console.log(this.props.chatid);控制台日志(this.props.buddy);this.setState({加载:this.props.loading,chatid: this.props.chatid,好友:this.props.buddy})}componentWillReceiveProps() {console.log(this.props.chatid);控制台日志(this.props.buddy);this.setState({加载:this.props.loading,chatid: this.props.chatid,好友:this.props.buddy})}使成为() {让盒子;如果(this.props.loading === true){box = <div class="loader"></div>}别的 {如果 (this.props.buddy !== "") {框 = <div><MatchingWindow chatid={this.state.chatid}/>

}别的 {box = <span>对不起</span>}}返回 (<div>{盒子}

);}}const mapStateToProps = (状态) =>{返回 {身份验证:state.firebase.auth,加载:state.auth.loading,聊天ID:state.auth.chatid,好友:state.auth.buddy,配置文件:state.firebase.profile};}导出默认连接(mapStateToProps)(MatchingBox);

如上所述,我很确定我的 redux actions 和 reducer 很好,但只是想让我的描述更容易理解.

Redux 操作

export const startMatching = (userid, userprofile, usergender,genderpreference) =>(调度) =>{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) =>(调度,getState) =>{......then(() => {dispatch({ type: CHAT_SUCCESS, payload: doc.id });}).then(() => {dispatch({ type: MATCHING_SUCCESS, payload: user2id });})}

Redux 减速器

const initialState = {加载:假,聊天:"",伙伴: "",}const authReducer = (state = initialState, action) =>{const {类型,有效载荷} = 动作;开关(类型){......案例 CHAT_SUCCESS:返回 {...状态,chatid: action.payload}案例 MATCHING_REQUEST:返回 {...状态,加载:真实}案例 MATCHING_SUCCESS:返回 {...状态,好友:action.payload,加载:假}案例 MATCHING_FAIL:返回 {...状态,好友:action.payload,加载:假}默认:返回状态;}}

解决方案

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

//main.jsconst WaitingRoomPage = () =>{return <WaitingRoom/>;};...<Route path='/waitingroom' component={WaitingRoomPage}/>

我认为您不应该这样做,因为每次重新渲染 Main 都会导致您的 WaitingRoom 保持 重新安装(并因此重新初始化).你的 WaitingRoom 不是一个功能组件,即使它是,你仍然应该使用更简单的方式来声明一个 Route 和一个 component属性:

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. Initially, after MATCHING_REQUEST was dispatched and "loading" was set to true, the state of WaitingRoom component still remained unchanged. 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. This is weird and did not happen with my other Redux actions. 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

///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. 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. 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. The chat window is the MatchingWindow component, which needs a chatid as its props to render the corresponding chat history between 2 users.

I am sure that my Redux actions work fine but got problems when passing the updated Redux state to my React components. Below are 2 approaches that I tried:

Approach 1: Display the chat window inside a modal returned by the WaitingRoom component. 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. Checking the props to the WaitingRoom component, I found that "buddy" and "chatid" were indeed updated. Shouldn't MatchingWindow be rendered? I also tried just putting success> instead of the MatchingWindow component and it was the same outcome.

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. 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. 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. As a result of componentWillReceiveProps() not being fired, the chatid I passed to the MatchingWindow component was an empty string, which is problematic. I tried componentDidUpdate() and it did not work either.

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)

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 actions

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

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

解决方案

You seem to be using a function to return the component:

// 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). 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:

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

这篇关于react组件中显示redux状态变化的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆