导航栏不会在路由器位置更改时重新呈现

Navbar is not re-rendering on router location change

我想在用户登录时显示用户名并将导航栏(导航组件)中的按钮文本从 'login' 更改为 'logout'。我厌倦了通过按下来重新呈现导航组件用户登录时主页的路由器历史记录位置。

this.props.history.push('/')

然后用户可以成功登录,用户数据保存在localStorage中,路由器位置也改变了。数据保存在 localStorage 中,但组件没有重新渲染。

这是我试过的。

  1. 试图将 'connected-react-router' 降级为 'v6.0.0' 但它给我带来了一些错误。所以把它放回最新版本'^6.4.0'。

  2. 试图在父组件 App.js 中添加导航栏代码。 但是 app.js 本身不会在用户登录时重新呈现。

app.js

import React from 'react';
import { connect } from 'react-redux';
/* --- Components --- */
import Nav from './src/components/nav';
import Loader from './src/shared/loader';
import './styles/main.scss';

const FlashMessagesContainer = Loader({
  loader: () =>
    import('./src/shared/flassMessagesContainer' /* webpackChunkName: 'FlashMessagesContainer' */),
});

const App = (props, { isOnModal }) => (
  <div id="app">
    <Nav />
    {!isOnModal && (
      <div className="flex justify-center">
        <FlashMessagesContainer />
      </div>
    )}
    {props.children}
  </div>
);

const mapPropsToState = state => ({
  isOnModal: state.modal.show,
});

export default connect(
  mapPropsToState,
  null,
)(App);

nav.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';

/* --- Components --- */
import {
  isLoggedIn,
  getCompanyName,
  clearLocalStorage,
} from '../../localStorage';

class Nav extends Component {

  handleUserLogout = async ev => {
    ev.preventDefault();
    await clearLocalStorage();
    return this.props.history.push('/login');
  };

  render() {
    return (
      <div className="nav">  
          {isLoggedIn() ? (
            <div className="flex">
              <p className="mr3 mt2">
                안녕하세요. <span className="b">{getCompanyName()}</span>
                &#8201;님,
              </p>
              <button
                type="button"
                className="login-btn td-none c-text br f-mini"
                onClick={this.handleUserLogout}
              >
                로그아웃
              </button>
            </div>
          ) : (
            <Link className="login-btn td-none c-text br f-mini" to="/login">
              로그인
            </Link>
          )}
      </div>
    );
  }
}

export default withRouter(Nav);

localStorage.js

export const getToken = () => localStorage.token;
export const getCompanyName = () => localStorage.companyName;
export const isLoggedIn = () => !!localStorage.token;
export const saveUserNameAndToken = userData => {
  localStorage.setItem('token', userData.token);
  localStorage.setItem('companyName', userData.companyName);
};
export const clearLocalStorage = () => localStorage.clear();

index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'connected-react-router';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import configureStore, { history } from './store';
import Routes from './routes';

const theme = createMuiTheme({
 ...
});

const store = configureStore();
const root = document.createElement('div');
document.body.appendChild(root);

render(
  <Provider store={store}>
    <MuiThemeProvider theme={theme}>
      <Router history={history}>
        <Routes />
      </Router>
    </MuiThemeProvider>
  </Provider>,
  root,
);

routes.js

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { hot, setConfig } from 'react-hot-loader';
import { withRouter } from 'react-router';
/* --- Components --- */
import App from './app';
import Loader from './src/shared/loader';

const Home = Loader({
  loader: () =>
    import('./src/components/home/homeContainer' /* webpackChunkName: 'Home' */),
});

...

const routes = props => (
  <div>
    <App history={props.history} />
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/login" render={props => <Login {...props} />} />
      <Route path="/users/account" component={Users} />
      <Route component={NoMatch} />
    </Switch>
  </div>
);

const Routes =
  !module.hot || process.env.NODE_ENV === 'production'
    ? routes
    : hot(module)(routes);

export default withRouter(Routes);

我希望在路由器位置更改时重新呈现 Nav 组件。

react: ^16.8.6
react-dom: ^16.8.6
react-loadable: ^5.5.0
react-redux: ^6.0.0
react-router-dom: ^4.3.1
redux: ^4.0.0
connected-react-router: ^6.4.0

我通过在 redux 存储中保存 'isLoggedIn' 状态来修复它。为了在页面刷新时保持 redux store 的状态,我使用了 'redux-persist'.

现在 Nav 组件 re-rendering 在路由器位置更改时,即使在页面刷新时也会保存 userName 数据。

authReducer.js

const initialState = {
  isLoggedIn: false,
  userName: '',
};
const auth = (state = initialState, action) => {
  switch (action.type) {
    case types.USER_LOGIN:
      return {
        ...state,
        isLoggedIn: true,
        userName: action.payload,
      };
    case types.USER_LOGOUT:
      return {
        ...state,
        isLoggedIn: false,
        userName: '',
      };
    default:
      return state;
  }
};

nav.js

...
{this.props.isLoggedIn ? (
           ...
          ) : (
           ...
          )}
...

const mapStateToProps = state => ({
  isLoggedIn: state.auth.isLoggedIn,
  userName: state.auth.userName,
});

const mapDispatchToProps = dispatch => ({
  userLogout: () => dispatch(userLogout()),
});

export default withRouter(
  connect(
    mapStateToProps,
    mapDispatchToProps,
  )(Nav),
);

[redux-persist设置]

store.js

import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import { routerMiddleware } from 'connected-react-router';
import createBrowserHistory from 'history/createBrowserHistory';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import createRootReducer from './src/reducers';

export const history = createBrowserHistory();

const persistConfig = {
  key: 'root',
  storage,
};
const persistedReducer = persistReducer(
  persistConfig,
  createRootReducer(history), 
);

const store = createStore(
  persistedReducer,
  composeWithDevTools(
    applyMiddleware(
      routerMiddleware(history), 
      thunkMiddleware,
      createLogger({
        predicate: () => process.env.NODE_ENV === 'development',
        collapsed: true,
      }),
    ),
  ),
);

export const persistor = persistStore(store);
export default store;

index.js

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'connected-react-router';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
import { PersistGate } from 'redux-persist/lib/integration/react';
import store, { history, persistor } from './store';
import Routes from './routes';
import Loading from './src/shared/loading';

const theme = createMuiTheme({
  palette: {
    primary: { main: '#AE9A65' },
    secondary: { main: '#ee9105' },
    success: { main: '#43A047' },
    warning: { main: '#FFA000' },
    error: { main: '#ed4337' },
    info: { main: '#2196F3' },
  },
});

const root = document.createElement('div');
document.body.appendChild(root);

render(
  <Provider store={store}>
    <PersistGate loading={<Loading />} persistor={persistor}>
      <MuiThemeProvider theme={theme}>
        <Router history={history}>
          <Routes />
        </Router>
      </MuiThemeProvider>
    </PersistGate>
  </Provider>,
  root,
);