React Lazy loading
Good day.
I am developing a 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 reducers, create a singleton reducerRegister class:
./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, reducers 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 reducers into the store.
Main file
Add routes and connect 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 (
}>
}/>
}}/>
page not found}
/>
)
}
}
if (document.getElementById('app')) {
ReactDOM.render(
,
document.getElementById('app')
)
}
Using React.lazy we make lazy loading of components. React.lazy is available starting from version 16.6: React. Lazy loading. The Suspense element is handling component loading.
AdminModule can only be downloaded by an authorized user, for this we use the RouteAdmin component:
./RouteAdmin.js
const NotAccess = (props) => {
return (
Доступ закрыт
)
}
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 (
)
}
}
Module implementation
Main file - add module routes
./moduleAdmin/Index.js
export default class IndexComponent extends Component {
constructor (props) {
super(props)
}
render () {
return (
<>
...
)
}
}
./moduleAdmin/pages/Profiles.js
class Profiles extends Component {
componentDidMount() {
this.props.getInfo()
}
render() {
if (this.props.loading === Process.Start) {
return
}
if (this.props.loading === Process.Success) {
return (
Profiles
)
}
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
We 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.