【问题标题】:React-Redux re-render on dispatch inside HOC not workingReact-Redux 在 HOC 内部调度时重新渲染不起作用
【发布时间】:2019-02-14 05:12:50
【问题描述】:

我正忙于概念验证,基本上要求是在用户尚未登录时将主页作为登录屏幕,然后在状态更改时显示具有相关内容的组件验证成功后。

我必须提前声明,我对 react 和 redux 非常陌生,并且正忙于学习教程以提高我的技能。但是,本教程有点基础,因为它不涉及连接服务器以完成任务。

我的第一个问题是让道具在fetch 的最后一个then 的上下文中可用,因为我收到this.props.dispatch 未定义的错误。我使用了旧的 javascript 技巧,如果我在最后的 then 中添加一个 console.log,我可以看到它不再是未定义的,实际上是一个预期的函数。

我现在的问题是当dispatch 被调用时什么都没有发生。但是,如果我手动刷新页面,它将按预期显示 AuthenticatedPartialPage 组件,因为 localstorage 已填充。

我的理解是,在调用dispatch 时,将重新评估条件语句并显示AuthenticatedPartialPage

感觉好像缺少了什么,dispatch 没有将更改传达回父组件,因此没有任何反应。这是否正确,如果正确,我将如何连接那段代码?

主页 HOC:

import React from 'react';
import { createStore, combineReducers } from 'redux';
import { connect } from 'react-redux';
import AuthenticatedPartialPage from './partials/home-page/authenticated';
import AnonymousPartialPage from './partials/home-page/anonymous';
import { loggedIntoApi, logOutOfApi } from '../actions/authentication';
import authReducer from '../reducers/authentication'

// unconnected stateless react component
const HomePage = (props) => (
    <div>
        { !props.auth 
            ? <AnonymousPartialPage /> 
            : <AuthenticatedPartialPage /> }
    </div>
);

const mapStateToProps = (state) => {
    const store = createStore(
        combineReducers({
            auth: authReducer
        })
    );

    //  When the user logs in, in the Anonymous component, the local storage is set with the response
    //  of the API when the log in attempt was successful.
    const storageAuth = JSON.parse(localStorage.getItem('auth'));
    if(storageAuth !== null) {

        //  Clear auth state in case local storage has been cleaned and thus the user should not be logged in.
        store.dispatch(logOutOfApi());

        //  Make sure the auth info in local storage is contained in the state.auth object.
        store.dispatch(loggedIntoApi(...storageAuth))
    }

    return {
        auth: state.auth && state.auth.jwt && storageAuth === null 
            ? state.auth 
            : storageAuth
    };
}

export default connect(mapStateToProps)(HomePage);

匿名 LOC 为:

import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { loggedIntoApi } from '../../../actions/authentication';

export class AnonymousPartialPage extends React.Component {
    constructor(props) {
        super(props);
    }
    onSubmit = (e) => {
        e.preventDefault();

        const loginData = { ... };

        //  This is where I thought the problem initially occurred as I 
        //  would get an error that `this.props` was undefined in the final 
        //  then` of the `fetch`. After doing this, however, the error went
        //  away and I can see that `props.dispatch is no longer undefined 

        //  when using it. Now though, nothing happens.
        const props = this.props;

        fetch('https://.../api/auth/login', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(loginData)
        })
        .then(function(response) {
            return response.json();    
        })
        .then(function(data) {
            if(data && data.jwt) {     
                props.dispatch(loggedIntoApi(data));
                localStorage.setItem('auth', JSON.stringify(data));
            }
            //  else show an error on screen 
        });
    };
    render() {
      return (
        <div>
             ... onSubmit gets called successfully somewhere in here ...
        </div>
      );
    }
}

export default connect()(AnonymousPartialPage);

动作:

// LOGGED_INTO_API
export const loggedIntoApi = (auth_token) => ({
    type: 'LOGGED_INTO_API',
    auth: auth_token
});

// LOGGED_OUT_OF_API
export const logOutOfApi = (j) => ({
    type: 'LOG_OUT_OF_API'
});

最后是减速器:

const authDefaultState = { };

export default (state = authDefaultState, action) => {
  switch (action.type) {
    case 'LOGGED_INTO_API':
      // SOLUTION : changed this line "return action.auth;" to this:
      return { ...action.auth, time_stamp: new Date().getTime() }
    case 'LOG_OUT_OF_API':
      return { auth: authDefaultState  };
    default:
      return state;
  }
};

【问题讨论】:

    标签: react-redux


    【解决方案1】:

    我的建议是确保您在 Redux 中更改的状态正在更改根据 javascript 的相等运算符!。发布的另一个问题有一个非常好的答案,它捕捉了这个想法here。基本上,您不能改变旧对象并将其发送回 Redux 并希望它重新渲染,因为与旧对象的相等性检查将返回 TRUE,因此 Redux 认为没有任何改变!我必须通过使用更新的值创建一个全新的对象并通过 dispatch() 发送它来解决这个问题。

    基本上:

    x = {
      foo:bar
    }
    
    x.foo = "baz"
    
    dispatch(thereWasAChange(x)) // doesn't update because the x_old === x returns TRUE!

    相反,我创建了一个新对象:

    x = {
      foo:"bar"
    }
    
    y = JSON.parse(JSON.stringify(x)) // creates an entirely new object
    
    dispatch(thereWasAChange(y)) // now it should update x correctly and trigger a rerender
    
    // BE CAREFUL OF THE FOLLOWING!
    
    y = x
    
    dispatch(thereWasAChange(y)) // This WON'T work!!, both y and x reference the SAME OBJECT! and therefore will not trigger a rerender

    希望这会有所帮助!

    【讨论】:

    • 我将此标记为答案,因为您为我指出了正确的方向,谢谢。问题是 auth 令牌在 reducer 级别看起来仍然相同(我猜是 redux,即使它从一个空对象变成了明显不为空的对象。)为了解决这个问题,我在 auth 对象中添加了一个时间戳每次更新对象时都会更改。我将在上面的代码中进行更正,并将“损坏”的代码留在 cmets 中。
    • 很高兴我能帮上忙 :)
    猜你喜欢
    • 2019-07-19
    • 2018-02-15
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-29
    • 2021-05-29
    • 2019-06-18
    相关资源
    最近更新 更多