Я переношу свое приложение reactJs на redux/mobx. На данный момент у меня есть 3 компонента, каждый из которых имеет в своем состоянии следующую структуру:
name:string,
surname:string,
showError: boolean,
loading:boolean
Как видите, имеет смысл переместить имя/фамилию в централизованный репозиторий, чтобы сохранить глобальное состояние. Я не уверен в showError и загрузке: это логические значения, предназначенные для отображения сообщения об ошибке и счетчика внутри конкретного компонента.
Мне лично не нравится идея помещать такое «состояние, связанное с пользовательским интерфейсом» в централизованное состояние, я бы хотел, чтобы оно оставалось внутри конкретного компонента, поскольку другим компонентам никогда не понадобится доступ/обновление таких вещей.
Итак, моя идея заключается в следующем:
this.state = {showError, loading};
this.businessState = props.state;
Короче говоря, я продолжу обновлять «состояние» с помощью функции React setState(), оставив redux/mobx управление так называемым «бизнес-состоянием».
Может ли это быть хорошей практикой или я делаю что-то особенно плохое?
У меня лично нет проблем с этим, поскольку «загрузка» и «showError» относятся к состоянию, а не к компоненту пользовательского интерфейса. По сути, это говорит о том, что когда я заполняю свое состояние из вызова API, это уже сделано и что-то пошло не так? Компонент пользовательского интерфейса отображается соответствующим образом.
Я бы сделал showError строкой или строкой [] в зависимости от структуры ошибок, выдаваемых вашей системой BE.
Я бы также разбил ваше состояние на конкретные редьюсеры, которые выполняют определенные бизнес-функции и состояния загрузки на основе конкретных бизнес-функций, а не загружают отдельные компоненты на странице.
Я надеюсь, что это поможет в некотором роде.
Лично я бы посоветовал сделать store
для большинства components
, и, исходя из моего опыта, большинство stores
на самом деле являются ViewStore's
. Если у вас есть похожая логика, такая как loading
и errors
на нескольких components
, то сделать что-то вроде этого еще проще:
class BaseViewStore {
@observable loading = false
@observable showError = false
}
Тогда ваш component store
просто расширит этот store
, и у вас будет многоразовая логика внутри этого base store
. Все, что является component
конкретным, конечно же, пойдет в этом конкретном store
. Таким образом, ваш components
будет чистым, не будет иметь personal state
(внутри самого component
) и его можно будет легко заменить другими компонентами, поскольку ваше поведение (состояние) будет в stores
.
Я не уверен, что это лучшая практика или нет. Но состояние загрузки используется, когда вы выполняете какое-либо асинхронное действие, например, получаете данные из API. Лучшая практика для получения таких данных — это действие. И то, что я обычно делаю внутри действия, — это отправка состояния загрузки перед вызовом API и отправка редюсера, который устанавливает для загрузки значение false после получения данных.
Итак, на мой взгляд. Я бы сохранил состояние загрузки внутри избыточности вместо использования состояния компонента.
Это было бы что-то вроде этого
action.js
getData = () => {
return (dispatch) => {
dispatch({ type: LOADING });
callApiToGetData()
.then(data => {
dispatch({ type: SUCCESS, data })
})
.catch(e => {
dispatch({ type: ERROR, error: e })
})
}
}
редуктор.js
const initialState = {
data: null,
loading: false,
error: null
}
function reducer(state = initialState, action) {
switch (action.type) {
case LOADING:
return { ...state, loading: true }
case SUCCESS:
return { ...state, loading: false, data: action.data, error: null }
case FAIL:
return { ...state, loading: false, error: action.error }
default:
return state
}
Можно ли использовать async/await внутри редукторов? Или меня заставляют этим фрагментом с обещаниями? Я спрашиваю, потому что сейчас я использую mobx и не очень разбираюсь в редукции.
Можно использовать async/await в действии (не редуктор. Редуктор предназначен только для сопоставления данных после бизнес-логики из действия). Просто убедитесь, что вы используете версию babel и ecmascript, которую вы используете, поддерживает ее.
лишь бы добавить. Вам нужно использовать redux-thunk, чтобы сделать возможным выполнение таких асинхронных действий.
Если вы не можете извлечь выгоду из состояния пользовательского интерфейса в глобальном состоянии, оно, вероятно, принадлежит локальному состоянию.