2017-06-04 4 views
1

Ich habe eine Sidebar mit zwei Tasten, "Test" und "über". Test (Raketensymbol) wird bei '/ test' gerendert und Über (Home-Symbol) wird bei '/' gerendert.React-Router v4 Rendering falsche Komponente, aber richtig zusammenpassen

Sie befinden sich beide im Stamm der App und sind in einer Komponente verschachtelt.

Wenn ich bei '/' starte und auf den Link zu = "/ test" klicke, wird immer die 'About' Komponente geladen, und wenn ich die Props für die KomponenteDidMount von 'About' überprüfe, enthält das Match-Objekt Matchdaten für "/ test".

Nur wenn ich sie aktualisiere, wird die richtige Komponente, 'Test', wieder angezeigt. Irgendeine Idee, warum das passiert?

AppRoutes.js:

export class AppRoutes extends React.Component { 

    render() { 
    return (
     <div> 
     <Switch> 
      <Route 
      exact path="/" 
      render={(matchProps) => (
       <LazyLoad getComponent={() => import('pages/appPages/About')} {...matchProps} /> 
      )} 
      /> 
      <Route 
      path="/login" 
      render={(matchProps) => (
       <LazyLoad getComponent={() => import('pages/appPages/Login')} {...matchProps} /> 
      )} 
      /> 
      <Route 
      path="/register" 
      render={(matchProps) => (
       <LazyLoad getComponent={() => import('pages/appPages/Register')} {...matchProps} /> 
      )} 
      /> 
      <Route 
      path="/test" 
      render={(matchProps) => (
       <LazyLoad getComponent={() => import('pages/appPages/Test')} {...matchProps} /> 
      )} 
      /> 
... 

AboutPage.js & & TestPage.js (Duplikate Ausnahme Komponentenname):

import React from 'react'; 

import SidebarContainer from 'containers/SidebarContainer'; 
import SidebarPageLayout from 'styles/SidebarPageLayout'; 

export const About = (props) => { 
    console.log('About Loading: ', props); 
    return (
    <SidebarPageLayout> 
     <SidebarContainer /> 
     <div>About</div> 
    </SidebarPageLayout> 
); 
} 

export default About; 

SidebarContainer.js:

import React from 'react'; 
import PropTypes from 'prop-types'; 
import _ from 'lodash'; 

import Sidebar from 'sidebar/Sidebar'; 
import HamburgerButton from 'sidebar/HamburgerButton'; 
import AboutButton from 'sidebar/AboutButton'; 
import ProfileButton from 'sidebar/ProfileButton'; 
import TestButton from 'sidebar/TestButton'; 

export class SidebarContainer extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     sidebarIsOpen: false, 
     sidebarElements: [], 
    }; 
    } 

    componentDidMount() { 
    if (!this.props.authenticated) { 
     this.setState({ 
     sidebarElements: _.concat(this.state.sidebarElements, HamburgerButton, ProfileButton, AboutButton, TestButton), 
     }); 
    } 
    } 

    toggleSidebarIsOpenState =() => { 
    this.setState({ sidebarIsOpen: !this.state.sidebarIsOpen }); 
    } 

    render() { 
    const { authenticated, sidebarIsOpen, sidebarElements} = this.state; 
    return (
     <div> 
     <Sidebar 
      authenticated={authenticated} 
      sidebarIsOpen={sidebarIsOpen} 
      sidebarElements={_.isEmpty(sidebarElements) ? undefined: sidebarElements} 
      toggleSidebarIsOpenState={this.toggleSidebarIsOpenState} 
     /> 
     </div> 
    ); 
    } 
} 

SidebarContainer.propTypes = { 
    authenticated: PropTypes.bool, 
}; 

export default SidebarContainer; 

Sidebar .js:

import React from 'react'; 
import _ from 'lodash'; 
import PropTypes from 'prop-types' 

import SidebarStyles from '../styles/SidebarStyles'; 

export const Sidebar = (props) => { 
    if (props && props.sidebarElements) { 
    return (
     <SidebarStyles sidebarIsOpen={props.sidebarIsOpen}> 
     {_.map(props.sidebarElements, (value, index) => { 
      return React.createElement(
      value, 
      { 
       key: index, 
       authenticated: props.authenticated, 
       sidebarIsOpen: props.sidebarIsOpen, 
       toggleSidebarIsOpenState: props.toggleSidebarIsOpenState, 
      }, 
     ); 
     })} 
     </SidebarStyles> 
    ); 
    } 
    return (
    <div></div> 
); 
} 

Sidebar.propTypes = { 
    authenticated: PropTypes.bool, 
    sidebarIsOpen: PropTypes.bool, 
    sidebarElements: PropTypes.array, 
    toggleSidebarIsOpenState: PropTypes.func, 
}; 

export default Sidebar; 

TestButton.js:

import React from 'react'; 
import PropTypes from 'prop-types'; 
import Icon from 'react-fontawesome'; 
import { 
    Link 
} from 'react-router-dom'; 

export const TestButton = (props) => { 
    return (
    <Link to="/test"> 
     <Icon name='rocket' size='2x' /> 
    </Link> 
); 
} 

export default TestButton; 

AboutButton.js:

import React from 'react'; 
import PropTypes from 'prop-types'; 
import Icon from 'react-fontawesome'; 
import { 
    Link 
} from 'react-router-dom'; 

export const AboutButton = (props) => { 
    return (
    <Link to="/"> 
     <Icon name='home' size='2x' /> 
    </Link> 
); 
} 

export default AboutButton; 

keine Auffrischung, nur konstant Klicken auf den '/ test' Weg vom '/' Route:

enter image description here

nach Aktualisierung:

enter image description here

Edit:

Wurzelkomponenten:

Edit:

store.js:

import { 
    createStore, 
    applyMiddleware, 
    compose, 
} from 'redux'; 
import createSagaMiddleware from 'redux-saga'; 

import { rootReducer } from './rootReducers'; 
import { rootSaga } from './rootSagas'; 

// sagas 
const sagaMiddleware = createSagaMiddleware(); 

// dev-tools 
const composeEnhancers = typeof window === 'object' && (
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? (
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ 
) : compose 
); 

export function configureStore() { 
    const middlewares = [ 
    sagaMiddleware, 
    ]; 
    const store = createStore(
    rootReducer, 
    {}, 
    composeEnhancers(applyMiddleware(...middlewares)) 
); 

    sagaMiddleware.run(rootSaga); 
    return store; 
} 

export const store = configureStore(); 

index.js (root):

import React from 'react'; 
import { Provider } from 'react-redux'; 
import ReactDOM from 'react-dom'; 
import { BrowserRouter } from 'react-router-dom'; 

import { store } from './store'; 
import AppContainer from 'containers/AppContainer'; 

ReactDOM.render(
    <Provider store={store}> 
    <BrowserRouter> 
     <AppContainer /> 
    </BrowserRouter> 
    </Provider>, 
    document.getElementById('root') 
); 

AppContainer:

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

import { logout, verifyToken } from './actions'; 
import { selectAuthenticated, selectAuthenticating } from './selectors'; 
import AppRoutes from 'routes/AppRoutes'; 

export class AppContainer extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { loaded: false }; 
    } 

    componentDidMount() { 
    const token = localStorage.getItem('jwt'); 
    if (token) { 
     this.props.verifyToken(token,() => this.setState({ loaded: true })); 
    } else { 
     this.setState({ loaded: true }); 
    } 
    } 

    render() { 
    if (this.state.loaded) { 
     return (
     <AppRoutes 
      authenticated={this.props.authenticated} 
      authenticating={this.props.authenticating} 
      logout={this.props.logout} 
     /> 
    ); 
    } else { 
     return <div>Loading ...</div> 
    } 
    } 
} 

function mapStateToProps(state) { 
    return { 
    authenticated: selectAuthenticated(state), 
    authenticating: selectAuthenticating(state), 
    }; 
} 

function mapDispatchToProps(dispatch) { 
    return { 
    verifyToken: (token = '', callback = false) => dispatch(verifyToken(token, callback)), 
    logout:() => dispatch(logout()), 
    }; 
} 

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

Edit 2 für LazyLoad:

services/LazyLoad/index.js:

import React from 'react'; 

export class LazyLoad extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     AsyncModule: null, 
    }; 
    } 

    componentDidMount() { 
    this.props.getComponent() // getComponent={() => import('./someFile.js')} 
     .then(module => module.default) 
     .then(AsyncModule => this.setState({AsyncModule})) 
    } 

    render() { 
    const { loader, ...childProps } = this.props; 
    const { AsyncModule } = this.state; 

    if (AsyncModule) { 
     return <AsyncModule {...childProps} />; 
    } 

    if (loader) { 
     const Loader = loader; 
     return <Loader />; 
    } 

    return null; 
    } 
} 

export default LazyLoad; 
+1

Ich hatte gestern ein ähnliches Problem bei meinem Projekt. Mein hatte eine umhüllende untergeordnete Komponente , die redux verwendete und dies blockierte das erneute Rendern. Es wurde behoben, indem 'connect (...) (TopLayout) 'in' withRouter (connect (...) (TopLayout)) 'geändert wurde, damit es bei jeder Änderung der Route erneut gerendert wird. Mehr Infos hier: https://reacttraining.com/reac-router/web/api/withRouter. Sie zeigen uns nicht Ihre Implementierung, also bin ich mir nicht sicher, dass es das gleiche Problem ist. –

+1

Das fühlt sich so ähnlich an wie die richtige Antwort und ich möchte, dass es wahr ist, aber es funktioniert immer noch nicht für mich. Ich ging rüber und wickelte jeden Container und jede Komponente ein, die einen connect() - Aufruf mit withRouter hatten und es gibt mir immer noch den Fehler. –

+1

Dies könnte wegen 'LazyLoad' sein. Können Sie den 'LazyLoad' Code teilen? –

Antwort

3

Ihr Problem liegt bei LazyLoad Komponente. Für beide Pfade "/" oder "Test" ist die AppRoutes Komponente, die letztendlich gerendert wird, eine Komponente LazyLoad.Denn Route und Switch rendern nur bedingt ihre Kinder. React kann jedoch "/" LazyLoad Komponente und "/ test" LazyLoad Komponente nicht unterscheiden. So rendert es zum ersten Mal LazyLoad Komponente und ruft die componentDidMount auf. Wenn sich die Route jedoch ändert, wird sie von React als Prop-Änderung der zuvor gerenderten LazyLoad Komponente betrachtet. Es ruft nur componentWillReceiveProps der vorherigen LazyLoad Komponente mit neuen Requisiten auf, anstatt vorherige zu deaktivieren und eine neue zu mounten. Aus diesem Grund wird die Info-Komponente ständig angezeigt, bis die Seite aktualisiert wird.

Um dieses Problem zu lösen, wenn die getComponent Requisite geändert wurde, müssen wir das neue Modul mit getComponent innerhalb der componentWillReceiveProps laden. So können wir die LazyLoad wie folgt ändern, die eine gemeinsame Methode haben, Modul zu laden und es sowohl von componentDidMount als auch componentWillReceiveProps mit korrekten Requisiten aufzurufen.

import React from 'react'; 

export class LazyLoad extends React.Component { 
    constructor(props) { 
    super(props); 
    this.state = { 
     AsyncModule: null, 
    }; 
    } 

    componentDidMount() { 
    this.load(this.props); 
    } 

    load(props){ 
    this.setState({AsyncModule: null} 
    props.getComponent() // getComponent={() => import('./someFile.js')} 
     .then(module => module.default) 
     .then(AsyncModule => this.setState({AsyncModule})) 
    } 

    componentWillReceiveProps(nextProps) { 
    if (nextProps.getComponent !== this.props.getComponent) { 
     this.load(nextProps) 
    } 
    } 

    render() { 
    const { loader, ...childProps } = this.props; 
    const { AsyncModule } = this.state; 

    if (AsyncModule) { 
     return <AsyncModule {...childProps} />; 
    } 

    if (loader) { 
     const Loader = loader; 
     return <Loader />; 
    } 

    return null; 
    } 
} 

export default LazyLoad; 
+1

heiliger Mist, es hat funktioniert, danke! –

+2

@KyleTruong Ich bin froh, dass es dir geholfen hat. Ich werde erklären, warum, wenn ich etwas Zeit habe. –

+0

Nochmals vielen Dank, ich freue mich darauf. –

Verwandte Themen