Доброго времени суток.
Занимаюсь разработкой проекта на React и Redux. Хочу в этой статье описать архитектуру своего проекта.
Итак, начнем. Файловая структура:
Для подключения редьюсеров создаем класс 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
С помощью этого класса редьюсеры могут сами себя регистрировать в store.
Создаем 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
}
С помощью функции store.replaceReducer загружаем редьюсеры в store.
Основной файл
Добавляем маршруты и подключаем 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')
)
}
С помощью React.lazy делаем ленивую загрузку компонентов.React.lazy доступен, начиная с версии 16.6: React. Lazy loading. В элементе Suspense обрабатывается загрузка компонента.
AdminModule может загрузить только авторизованный пользователь, для этого используем компонент RouteAdmin:
./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}/>
)
}
}
Реализация модуля
Основной файл — добавляем маршруты модуля
./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)
Создаем редьюсер
Тут же регистрируем его в 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)
Надеюсь моя статья поможет в реализации вашего проекта, а ваши комментарии помогут улучшить мой.