📜 ⬆️ ⬇️

React. Lazy loading

Good day.


I am engaged in the development of the project on React and Redux. I want to describe the architecture of my project in this article.

So, let's begin. File structure:


')
To connect the reducer we create the class singleton reducerRegister:

./reducerRegister.js
class ReducerRegistry { constructor () { if (!ReducerRegistry.instance) { this._emitChange = null this._reducers = {} ReducerRegistry.instance = this } return ReducerRegistry.instance } getReducers () { return {...this._reducers} } register (name, reducer) { this._reducers = {...this._reducers, [name]: reducer} if (this._emitChange) { this._emitChange(this.getReducers()) } } setChangeListener (listner) { this._emitChange = listner } } const reducerRegistry = new ReducerRegistry() export default reducerRegistry 

With this class, viewers can register themselves in the store.

Create a store:


./configureStore
 export default function configureStore (initialState) { const combine = (reducers) => { const reducerNames = Object.keys(reducers) Object.keys(initialState).forEach(item => { if (reducerNames.indexOf(item) === -1) { reducers[item] = (state = null) => state } }) reducers['router'] = connectRouter(history) return combineReducers(reducers) } const reducer = combine(reducerRegistry.getReducers()) const store = createStore(reducer, initialState, compose(composeWithDevTools(applyMiddleware(thunk)), applyMiddleware(routerMiddleware(history)))) reducerRegistry.setChangeListener(reducers => { store.replaceReducer(combine(reducers)) }) return store } 

Using the store.replaceReducer function, load the reducers into the store.

Main file


Add routes and enable redux

./index.js
 const Cabinet = React.lazy(() => import('./moduleCabinet/Index')) let store = configureStore({ profile: {loading: null} }) class App extends Component { render () { const history = createBrowserHistory() return ( <Router basename="/"> <ConnectedRouter history={history}> <Suspense fallback={<Loader/>}> <Switch> <Route exact path="/" component={Main} /> <Route path="/admin" render={(props) => <RouteAdmin {...props} />}/> <Route path="/cabinet" component={props => <Cabinet {...props} />}}/> <Route path="/" component={() => <div>page not found</div>} /> </Switch> </Suspense> </ConnectedRouter> </Router> ) } } if (document.getElementById('app')) { ReactDOM.render( <Provider store={store}> <App/> </Provider>, document.getElementById('app') ) } 

With the help of React.lazy we are doing lazy loading of components. React.lazy is available starting from version 16.6: React. Lazy loading. The Suspense element handles component loading.

AdminModule can only be downloaded by an authorized user, for this we use the RouteAdmin component:

./RouteAdmin.js
 const NotAccess = (props) => { return ( <div> <h1 className="text-danger"> </h1> </div> ) } export default class RouteAdmin extends Component { constructor (props) { super(props) this.state = { component: null } } componentDidMount () { axios.post('/admin').then(data => data.data).then(data => { if (data.auth === true) { const Admin = React.lazy(() => import('./moduleAdmin/Index')) this.setState({component: Admin}) } else { this.setState({component: NotAccess}) } }) } render () { const Component = this.state.component return ( <Route path="/admin" {...this.props} render={this.state.component}/> ) } } 

Module implementation


Main file - add module routes

./moduleAdmin/Index.js
 export default class IndexComponent extends Component { constructor (props) { super(props) } render () { return ( <> <Route to="/admin/Profiles" component={Profiles} /> ... </> ) } } 

./moduleAdmin/pages/Profiles.js
 class Profiles extends Component { componentDidMount() { this.props.getInfo() } render() { if (this.props.loading === Process.Start) { return <Loader /> } if (this.props.loading === Process.Success) { return ( <div> <h1>Profiles</h1> </div> ) } return null } } const mapStateToProps = (state) => { return { loading: state.profiles.loading } } const mapDispatchToProps = (dispatch) => { return { getInfo: () => dispatch(getInfo()) } } export default connect( mapStateToProps, mapDispatchToProps )(Profiles) 

Create a reducer


Immediately register it in the store:

./moduleAdmin/redux/profile.js
 const Process = { Start: 0, Success: 1, Error: 2 } export const getInfo = () => { return (dispatch) => { dispatch({ type: PROFILES_GET_START }) axios.post('/news').then((data) => { dispatch({ type: PROFILES_GET_SUCCESS, payload: data.data }) }).catch(e => { dispatch({ type: PROFILES_GET_ERROR, payload: e }) }) } } const initialState = { error: null, loading: null, data: null } const reducer = (state = initialState, action) => { switch (action.type) { case PROFILES_GET_START: { return { ...state, loading: Process.Start } } case PROFILES_GET_SUCCESS: { return { ...state, loading: Process.Success, data: action.payload} } case PROFILES_GET_ERROR: { return { ...state, loading: Process.Error, error: action.payload } } default: { return state } } } reducerRegistry.register('profiles', reducer) 

I hope my article will help in the implementation of your project, and your comments will help improve mine.

Source: https://habr.com/ru/post/453294/


All Articles