[Разработка веб-сайтов, ReactJS] React — Используйте стандартные пропсы для потока данных
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Давайте поговорим про поток данных React приложения состоящего из набора форм.
Предполагается, что читатель знаком с react, react-хуками, функциональными компонентами, мемоизацией хорошо знает javascript и не пугается spread операторов (три точки которые).
Я постараюсь подвести вас к логичному для меня выводу, что зачастую компонентам, которые отображают форму или часть формы, достаточно иметь всего три пропса.
Представьте себе сложную форму которая состоит из нескольких частей, а те в свою очередь состоят из других частей.
Например форма редактирования данных о пользователе:
const UserForm = () =>
<FormBlock>
<UserInfo/>
<Experience/>
<Education/>
</FormBlock>
В компоненте UserInfo редактируются поля firstName, lastName.
В компоненте Experience редактируются поля positionName, positionDescription.
В компоненте Education редактируются поля name, description.
Попробуем реализовать компонент UserInfo
Иногда я встречаю такую реализацию:
const UserInfo = ({
firstName,
onChangeFirstName,
lastName,
onChangeLastName,
}) =>
<FormBlock>
<Label>First Name</Label>
<Input
value={firstName}
onChange={({ target: { value } }) => onChangeFirstName(value)}
/>
<Label>Last Name</Label>
<Input
value={lastName}
onChange={({ target: { value } }) => onChangeLastName(value)}
/>
</FormBlock>
И такой вызов из UserForm:
const UserForm = ({
firstName,
onChangeFirstName,
lastName,
onChangeLastName,
}) =>
<FormBlock>
<UserInfo
firstName={firstName}
onChangeFirstName={onChangeFirstName}
lastName={lastName}
onChangeLastName={onChangeLastName}
/>
</FormBlock>
Но не делайте так. Если пойдет в таком духе, то на входе у UserForm будут все пропсы из компонентов UserInfo, Experience и Education. Мне даже лень этот код вам писать.
Обычно всем тоже лень и в итоге просто вместо прописывания всех пропсов используют spread оператор:
const UserForm = (props) =>
<FormBlock>
<UserInfo {...props} />
<Experience {...props} />
<Education {...props} />
</FormBlock>
И дальше надеются, что каждый компонент сам выберет себе нужные пропсы.
Пожалуйста, так тоже не делайте. Вы подвергаете свой код неявным ошибкам. Мало ли что может залететь в UserForm что не желательно чтобы было в Education. Ну например пропс className или style которые пол года назад использовались чтобы стилизовать UserForm, потом в UserForm это убрали, а в Education такой пропс добавили. И вот кто-то забыл почистить код и где-то остались вызовы UserForm с className. Теперь неожиданно для всех className прокинется в Education.
Всегда явно прокидуйте пропсы чтобы это видно было по коду какие пропсы в какие компоненты попадают.
Что мы можем с этим всем сделать?
Давайте посмотрим на обычные поля ввода которые перекочевали в реакт из HTML. Огромное спасибо разработчикам реакта, что сохранили тот же интерфейс привычный всем, а не, как в ангуляре, напридумывали своих конструкций.
Возьмем, например, тег input. У него есть знакомые всем пропсы: value, onChange и, внимание, name .
По сути — это все три пропсы достаточные для передачи потока данных.
Вот так например будет выглядеть теперь UserInfo:
const UserInfo = ({
name,
value,
onChange,
}) =>
<FormBlock>
<Label>First Name</Label>
<Input
name={'firstName'}
value={value['firstName']}
onChange={({ target }) => onChange({target: { name, value: { ...value, [target.name]: target.value } }})}
/>
<Label>Last Name</Label>
<Input
value={lastName}
onChange={({ target: { value } }) => onChangeLastName(value)}
/>
</FormBlock>
Тут я в компоненте UserInfo использую стандартные три пропса. И что немаловажно повторил интерфейс вызова события onChange. Он так же само возвращает информацию о изменениях как это делает стандартный input используя target, name, value. С одной стороны target добавляет еще уровень вложенности, но так уж исторически сложилось у стандартного события onChange, с этим уже ничего не поделаешь. Зато мы получаем очень важное преимущество — одинаковое поведение всех полей ввода и частей формы.
То есть мы можем теперь переписать UserForm.
Если у нас данные хранятся как такой объект:
{ firstName, lastName, positionName, positionDescription, name, description }
То пишем так:
const UserForm = ({
name,
value,
onChange,
}) =>
<FormBlock>
<UserInfo
value={value}
onChange={({ target }) => onChange({target: { name, value: target.value }})}
/>
.......
</FormBlock>
Если у нас данные хранятся как такой объект:
{
userInfo: { firstName, lastName },
position: { positionName, positionDescription },
education: { name, description }
}
То пишем так:
const UserForm = ({
name,
value,
onChange,
}) =>
<FormBlock>
<UserInfo
name={'userInfo'}
value={value['userInfo']}
onChange={({ target }) => onChange({target: { name, value: { ...value, [target.name]: target.value } }})}
/>
.......
</FormBlock>
Как видим, количество пропсов на входе UserForm уменьшилось с 2*N до всего трех.
Но это только часть выгоды.
Как сделать код компактней и читабельней?
Так как у нас теперь везде одинаковый интерфейс, то теперь можно написать вспомогательные функции, которые будут работать со всеми такими компонентами.
Например, представим функцию getInnerProps, которая мапит вложенные данные на вложенные компоненты. Тогда код компонентов становится намного лаконичней:
const UserInfo = ({ name, value, onChange }) => {
const innerProps = getInnerProps({name, value, onChange})
return <FormBlock>
<Label>First Name</Label>
<Input {...innerProps.forInput('firstName')} />
<Label>Last Name</Label>
<Input {...innerProps.forInput('lastName')} />
</FormBlock>
}
const UserForm = ({
name,
value,
onChange,
}) => {
const innerProps = getInnerProps({name, value, onChange})
return <FormBlock>
<UserInfo {...innerProps.forInput('userInfo')} />
<Experience {...innerProps.forInput('position')} />
<Education {...innerProps.forInput('education')} />
</FormBlock>
}
Обратите внимание, что одна и та же функция innerProps.forInput() формирует пропсы name, value и onChange и для стандартного поля ввода Input и для компонента UserInfo. Все благодаря одному интерфейсу потока данных.
Усложним пример
Допустим, пользователю нужно ввести несколько образований (education). Один из вариантов решения (на мой взгляд неправильного):
const UserForm = ({
educations,
onChangeEducation,
}) =>
<FormBlock>
{Object.entries(educations).map(([id, education]) => <Education
name={name}
description={description}
onChangeName={(name) => onChangeEducation(id, { ...education, name })}
onChangeDescription={(description) => onChangeEducation(id, { ...education, description })}
/>}
</FormBlock>
Обработчик onChangeEducation будет менять в нужном месте стора education по его id. Тут есть небольшое противоречие. На вход прилетает коллекция educations, а на событие изменения возвращается один education.
Можно часть кода перенести из Redux в компонент. Тогда станет все логичней. На вход UserForm приходит коллекция educations и на событие изменения уходит тоже коллекция educations:
const UserForm = ({
educations,
onChangeEducations,
}) =>
<FormBlock>
{Object.entries(educations).map(([id, education]) => <Education
name={name}
description={description}
onChangeName={(name) => onChangeEducations({ ...educations, [id]: { ...education, name } })}
onChangeDescription={(description) => onChangeEducations({ ...educations, [id]: { ...education, description } })}
/>}
</FormBlock>
Немного остановимся на том как мы передаем обработчик в onChangeName и onChangeDescription. Я сознательно не обращал на это внимание для минимизации примеров. Но сейчас это важно.
В реальности компонент Education будет скорее всего мемоизированный (React.memo()). Тогда мемоизация не будет иметь смысла из-за того, что каждый раз мы передаем новую ссылку на функцию. Чтобы не создавать каждый раз новую ссылку используют хук useCallback или useConstant (отдельный npm модуль).
Если в остальных примерах это решало проблему, то тут цикл, а хуки нельзя использовать внутри условий и циклов.
А вот используя name и ожидая от Education стандартного поведения onChange уже можно применить хук useConstant:
const UserForm = ({
name,
value,
onChange,
}) => {
const onChangeEducation=({ target }) => useConstant(onChange({
target: {
name,
value: {
...value,
educations: { ...value.educations, [target.name]: target.value ] }
}
}
}))
return <FormBlock>
{Object.entries(educations).map(([id, education]) => <Education
name={id}
value={education}
onChange={onChangeEducation}
/>
)}
</FormBlock>
А теперь сделаем с помощью функции getInnerProps:
const Education = ({ name, value, onChange }) => {
const innerProps = getInnerProps({name, value, onChange})
return <FormBlock>
<Label>Name</Label>
<Input {...innerProps.forInput('name')} />
<Label>Description</Label>
<Input {...innerProps.forInput('description')} />
</FormBlock>
}
const Educations = ({ name, value, onChange }) => {
const innerProps = getInnerProps({name, value, onChange})
return Object.keys(value).map((id) =>
<Education {...innerProps.forInput('id')} />
)
}
const UserForm = ({
name,
value,
onChange,
}) => {
const innerProps = getInnerProps({name, value, onChange})
return <FormBlock>
<UserInfo {...innerProps.forInput('userInfo')} />
<Experience {...innerProps.forInput('position')} />
<Educations {...innerProps.forInput('educations')} />
</FormBlock>
}
Вроде как достаточно красивый и понятный код получился.
Несколько слов про стейт
Подключим stateless компонент UserInfo к стейту и замкнем поток данных. В качестве примера возьмем Redux.
Вот так иногда реализовывают редюсер:
const reducer = (state = initState, action) {
switch(action.type) {
case CHANGE_FIRST_NAME:
return { ...state, userInfo: { ...state.userInfo, firstName: action.payload } }
case CHANGE_LAST_NAME:
return { ...state, userInfo: { ...state.userInfo, lastName: action.payload } }
........
}
}
В этом подходе я вижу два сомнительных плюса и один большой минус.
Первый плюс — это то, что можно написать тест для этого редсера. Сомнителен — потому что вряд ли этот тест сильно поможет.
Второй плюс — то что можно отдельно подконектить чуть ли не каждый инпут к отдельному полю в сторе и будет обновлятся только это связанное поле ввода. Тут еще не факт что это даст увеличение производительности. Проитерироваться по 10 мемоизированным частям формы, в результате чего будет перерисована только одна часть — это практически не скажется на производительности.
Минус в том что придется писать очень много кода: для каждого поля изменение стейта, потом добавить action, прокинуть значение, вызвать на каждое событие отдельный action.
Да, в документации по редаксу говорят, что надо писать редюсеры не такие у которых только set, а такие в которых побольше екшенов. Типа чем больше экшенов в реюсере, тем больше тестов можно написать. Больше тестов — меньше ошибок.
Но я думаю меньше ошибок там, где меньше кода, а много экшенав нужно писать только там где это надо.
Для себя я решил что для форм в редаксе, везде где это возможно, использую только один action — какой-нибудь SET.
const reducer = (state = initState, action) {
switch(action.type) {
case SET_USER_FORM_DATA:
return { ...state, value: action.payload }
........
}
}
А уже на UI (т.е. в реакте) я определяю какие поля в какой части данных меняются.
const UserFormContainer = () => {
const dispatch = useDispatch()
return <UserForm
value={useSelector(({ userForm }) => userForm?.value)}
onChange={({target: { value } }) => dispatch(userFormActions.set(value)}
/>
}
Потому как логику специфических полей не опишешь в редаксе. Например поле ввода номера телефона — это может быть сложный реакт компонент компонент, а не просто изменение значение в стейте.
Когда применять описанный подход
Имейте ввиду. Это не подход на все случаи жизни. Все описанное применимо в основном к приложениям в которых много форм, где одна форма собирается из набора других форм и поток данных направлен из стора в форму контейнер, из нее — в составные части формы, из них еще — на уровень ниже.
Если у вас приложение со сложным интерфесом в котором разные компоненты взаимодействуют друг с другом описанное в статье мало что вам даст. Как раз в этом случае логично каждый компонент подключать к стору.
Если у вас смешанное приложение, то важно найти границу — какие части формы подключать к редаксу, а в каких пробрасывать данные от контейнера к дочерним компонентам. Обычно эта граница начинается там, где появляется логика взаимодействия разных частей формы.
Резюме
Давайте использовать одинаковые пропсы для потока данных, пропсы которые уже давно есть в HTML:
- name
- value,
- onChange({target: { name, value }})
Старайтесь в onChange придерживаться той же структуры, как и в реактовском onChange.
Старайтесь на onChange в target.value возвращать ту же сущность что на вход в value.
Тогда, за счет использования стандартного подхода и общих хелперных функций для этого подхода, код станет лаконичней и понятней.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, Разработка мобильных приложений, HTML] html2json
- [Информационная безопасность, JavaScript, Google Chrome, Браузеры] Google Chrome начнет блокировать JavaScript-редирект по кликам на ссылки
- [Разработка веб-сайтов, JavaScript, Программирование] 20+ ES6-сниппетов для решения практических задач (перевод)
- [Разработка веб-сайтов, Управление продуктом] Как мы создавали первый онлайн-сервис Автокредит
- [JavaScript, Программирование, Я пиарюсь, Lisp] Как я устал от JavaScript и создал свой собственный язык программирования
- [JavaScript, HTML, Node.JS] Написание графического приложения на Electron JS (начало: Создание окна)
- [Разработка веб-сайтов, JavaScript, Программирование] Пример практического использования модулей
- [Высокая производительность, Разработка веб-сайтов, JavaScript, Клиентская оптимизация, ReactJS] Производительность приложений, работающих с Video и Audio
- [Разработка веб-сайтов, JavaScript, VueJS] Автоматическое обновление скриптов после деплоя
- [Мессенджеры, JavaScript, Программирование, VueJS] Как создать приложение-чат за двадцать минут (перевод)
Теги для поиска: #_razrabotka_vebsajtov (Разработка веб-сайтов), #_reactjs, #_jsx, #_javascript, #_reactjs, #_react.js, #_razrabotka_vebsajtov (
Разработка веб-сайтов
), #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:57
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Давайте поговорим про поток данных React приложения состоящего из набора форм. Предполагается, что читатель знаком с react, react-хуками, функциональными компонентами, мемоизацией хорошо знает javascript и не пугается spread операторов (три точки которые). Я постараюсь подвести вас к логичному для меня выводу, что зачастую компонентам, которые отображают форму или часть формы, достаточно иметь всего три пропса. Представьте себе сложную форму которая состоит из нескольких частей, а те в свою очередь состоят из других частей. Например форма редактирования данных о пользователе: const UserForm = () =>
<FormBlock> <UserInfo/> <Experience/> <Education/> </FormBlock> В компоненте UserInfo редактируются поля firstName, lastName. В компоненте Experience редактируются поля positionName, positionDescription. В компоненте Education редактируются поля name, description. Попробуем реализовать компонент UserInfo Иногда я встречаю такую реализацию: const UserInfo = ({
firstName, onChangeFirstName, lastName, onChangeLastName, }) => <FormBlock> <Label>First Name</Label> <Input value={firstName} onChange={({ target: { value } }) => onChangeFirstName(value)} /> <Label>Last Name</Label> <Input value={lastName} onChange={({ target: { value } }) => onChangeLastName(value)} /> </FormBlock> И такой вызов из UserForm: const UserForm = ({
firstName, onChangeFirstName, lastName, onChangeLastName, }) => <FormBlock> <UserInfo firstName={firstName} onChangeFirstName={onChangeFirstName} lastName={lastName} onChangeLastName={onChangeLastName} /> </FormBlock> Но не делайте так. Если пойдет в таком духе, то на входе у UserForm будут все пропсы из компонентов UserInfo, Experience и Education. Мне даже лень этот код вам писать. Обычно всем тоже лень и в итоге просто вместо прописывания всех пропсов используют spread оператор: const UserForm = (props) =>
<FormBlock> <UserInfo {...props} /> <Experience {...props} /> <Education {...props} /> </FormBlock> И дальше надеются, что каждый компонент сам выберет себе нужные пропсы. Пожалуйста, так тоже не делайте. Вы подвергаете свой код неявным ошибкам. Мало ли что может залететь в UserForm что не желательно чтобы было в Education. Ну например пропс className или style которые пол года назад использовались чтобы стилизовать UserForm, потом в UserForm это убрали, а в Education такой пропс добавили. И вот кто-то забыл почистить код и где-то остались вызовы UserForm с className. Теперь неожиданно для всех className прокинется в Education. Всегда явно прокидуйте пропсы чтобы это видно было по коду какие пропсы в какие компоненты попадают. Что мы можем с этим всем сделать? Давайте посмотрим на обычные поля ввода которые перекочевали в реакт из HTML. Огромное спасибо разработчикам реакта, что сохранили тот же интерфейс привычный всем, а не, как в ангуляре, напридумывали своих конструкций. Возьмем, например, тег input. У него есть знакомые всем пропсы: value, onChange и, внимание, name . По сути — это все три пропсы достаточные для передачи потока данных. Вот так например будет выглядеть теперь UserInfo: const UserInfo = ({
name, value, onChange, }) => <FormBlock> <Label>First Name</Label> <Input name={'firstName'} value={value['firstName']} onChange={({ target }) => onChange({target: { name, value: { ...value, [target.name]: target.value } }})} /> <Label>Last Name</Label> <Input value={lastName} onChange={({ target: { value } }) => onChangeLastName(value)} /> </FormBlock> Тут я в компоненте UserInfo использую стандартные три пропса. И что немаловажно повторил интерфейс вызова события onChange. Он так же само возвращает информацию о изменениях как это делает стандартный input используя target, name, value. С одной стороны target добавляет еще уровень вложенности, но так уж исторически сложилось у стандартного события onChange, с этим уже ничего не поделаешь. Зато мы получаем очень важное преимущество — одинаковое поведение всех полей ввода и частей формы. То есть мы можем теперь переписать UserForm. Если у нас данные хранятся как такой объект: { firstName, lastName, positionName, positionDescription, name, description }
То пишем так: const UserForm = ({
name, value, onChange, }) => <FormBlock> <UserInfo value={value} onChange={({ target }) => onChange({target: { name, value: target.value }})} /> ....... </FormBlock> Если у нас данные хранятся как такой объект: {
userInfo: { firstName, lastName }, position: { positionName, positionDescription }, education: { name, description } } То пишем так: const UserForm = ({
name, value, onChange, }) => <FormBlock> <UserInfo name={'userInfo'} value={value['userInfo']} onChange={({ target }) => onChange({target: { name, value: { ...value, [target.name]: target.value } }})} /> ....... </FormBlock> Как видим, количество пропсов на входе UserForm уменьшилось с 2*N до всего трех. Но это только часть выгоды. Как сделать код компактней и читабельней? Так как у нас теперь везде одинаковый интерфейс, то теперь можно написать вспомогательные функции, которые будут работать со всеми такими компонентами. Например, представим функцию getInnerProps, которая мапит вложенные данные на вложенные компоненты. Тогда код компонентов становится намного лаконичней: const UserInfo = ({ name, value, onChange }) => {
const innerProps = getInnerProps({name, value, onChange}) return <FormBlock> <Label>First Name</Label> <Input {...innerProps.forInput('firstName')} /> <Label>Last Name</Label> <Input {...innerProps.forInput('lastName')} /> </FormBlock> } const UserForm = ({ name, value, onChange, }) => { const innerProps = getInnerProps({name, value, onChange}) return <FormBlock> <UserInfo {...innerProps.forInput('userInfo')} /> <Experience {...innerProps.forInput('position')} /> <Education {...innerProps.forInput('education')} /> </FormBlock> } Обратите внимание, что одна и та же функция innerProps.forInput() формирует пропсы name, value и onChange и для стандартного поля ввода Input и для компонента UserInfo. Все благодаря одному интерфейсу потока данных. Усложним пример Допустим, пользователю нужно ввести несколько образований (education). Один из вариантов решения (на мой взгляд неправильного): const UserForm = ({
educations, onChangeEducation, }) => <FormBlock> {Object.entries(educations).map(([id, education]) => <Education name={name} description={description} onChangeName={(name) => onChangeEducation(id, { ...education, name })} onChangeDescription={(description) => onChangeEducation(id, { ...education, description })} />} </FormBlock> Обработчик onChangeEducation будет менять в нужном месте стора education по его id. Тут есть небольшое противоречие. На вход прилетает коллекция educations, а на событие изменения возвращается один education. Можно часть кода перенести из Redux в компонент. Тогда станет все логичней. На вход UserForm приходит коллекция educations и на событие изменения уходит тоже коллекция educations: const UserForm = ({
educations, onChangeEducations, }) => <FormBlock> {Object.entries(educations).map(([id, education]) => <Education name={name} description={description} onChangeName={(name) => onChangeEducations({ ...educations, [id]: { ...education, name } })} onChangeDescription={(description) => onChangeEducations({ ...educations, [id]: { ...education, description } })} />} </FormBlock> Немного остановимся на том как мы передаем обработчик в onChangeName и onChangeDescription. Я сознательно не обращал на это внимание для минимизации примеров. Но сейчас это важно. В реальности компонент Education будет скорее всего мемоизированный (React.memo()). Тогда мемоизация не будет иметь смысла из-за того, что каждый раз мы передаем новую ссылку на функцию. Чтобы не создавать каждый раз новую ссылку используют хук useCallback или useConstant (отдельный npm модуль). Если в остальных примерах это решало проблему, то тут цикл, а хуки нельзя использовать внутри условий и циклов. А вот используя name и ожидая от Education стандартного поведения onChange уже можно применить хук useConstant: const UserForm = ({
name, value, onChange, }) => { const onChangeEducation=({ target }) => useConstant(onChange({ target: { name, value: { ...value, educations: { ...value.educations, [target.name]: target.value ] } } } })) return <FormBlock> {Object.entries(educations).map(([id, education]) => <Education name={id} value={education} onChange={onChangeEducation} /> )} </FormBlock> А теперь сделаем с помощью функции getInnerProps: const Education = ({ name, value, onChange }) => {
const innerProps = getInnerProps({name, value, onChange}) return <FormBlock> <Label>Name</Label> <Input {...innerProps.forInput('name')} /> <Label>Description</Label> <Input {...innerProps.forInput('description')} /> </FormBlock> } const Educations = ({ name, value, onChange }) => { const innerProps = getInnerProps({name, value, onChange}) return Object.keys(value).map((id) => <Education {...innerProps.forInput('id')} /> ) } const UserForm = ({ name, value, onChange, }) => { const innerProps = getInnerProps({name, value, onChange}) return <FormBlock> <UserInfo {...innerProps.forInput('userInfo')} /> <Experience {...innerProps.forInput('position')} /> <Educations {...innerProps.forInput('educations')} /> </FormBlock> } Вроде как достаточно красивый и понятный код получился. Несколько слов про стейт Подключим stateless компонент UserInfo к стейту и замкнем поток данных. В качестве примера возьмем Redux. Вот так иногда реализовывают редюсер: const reducer = (state = initState, action) {
switch(action.type) { case CHANGE_FIRST_NAME: return { ...state, userInfo: { ...state.userInfo, firstName: action.payload } } case CHANGE_LAST_NAME: return { ...state, userInfo: { ...state.userInfo, lastName: action.payload } } ........ } } В этом подходе я вижу два сомнительных плюса и один большой минус. Первый плюс — это то, что можно написать тест для этого редсера. Сомнителен — потому что вряд ли этот тест сильно поможет. Второй плюс — то что можно отдельно подконектить чуть ли не каждый инпут к отдельному полю в сторе и будет обновлятся только это связанное поле ввода. Тут еще не факт что это даст увеличение производительности. Проитерироваться по 10 мемоизированным частям формы, в результате чего будет перерисована только одна часть — это практически не скажется на производительности. Минус в том что придется писать очень много кода: для каждого поля изменение стейта, потом добавить action, прокинуть значение, вызвать на каждое событие отдельный action. Да, в документации по редаксу говорят, что надо писать редюсеры не такие у которых только set, а такие в которых побольше екшенов. Типа чем больше экшенов в реюсере, тем больше тестов можно написать. Больше тестов — меньше ошибок. Но я думаю меньше ошибок там, где меньше кода, а много экшенав нужно писать только там где это надо. Для себя я решил что для форм в редаксе, везде где это возможно, использую только один action — какой-нибудь SET. const reducer = (state = initState, action) {
switch(action.type) { case SET_USER_FORM_DATA: return { ...state, value: action.payload } ........ } } А уже на UI (т.е. в реакте) я определяю какие поля в какой части данных меняются. const UserFormContainer = () => {
const dispatch = useDispatch() return <UserForm value={useSelector(({ userForm }) => userForm?.value)} onChange={({target: { value } }) => dispatch(userFormActions.set(value)} /> } Потому как логику специфических полей не опишешь в редаксе. Например поле ввода номера телефона — это может быть сложный реакт компонент компонент, а не просто изменение значение в стейте. Когда применять описанный подход Имейте ввиду. Это не подход на все случаи жизни. Все описанное применимо в основном к приложениям в которых много форм, где одна форма собирается из набора других форм и поток данных направлен из стора в форму контейнер, из нее — в составные части формы, из них еще — на уровень ниже. Если у вас приложение со сложным интерфесом в котором разные компоненты взаимодействуют друг с другом описанное в статье мало что вам даст. Как раз в этом случае логично каждый компонент подключать к стору. Если у вас смешанное приложение, то важно найти границу — какие части формы подключать к редаксу, а в каких пробрасывать данные от контейнера к дочерним компонентам. Обычно эта граница начинается там, где появляется логика взаимодействия разных частей формы. Резюме Давайте использовать одинаковые пропсы для потока данных, пропсы которые уже давно есть в HTML:
Старайтесь в onChange придерживаться той же структуры, как и в реактовском onChange. Старайтесь на onChange в target.value возвращать ту же сущность что на вход в value. Тогда, за счет использования стандартного подхода и общих хелперных функций для этого подхода, код станет лаконичней и понятней. =========== Источник: habr.com =========== Похожие новости:
Разработка веб-сайтов ), #_reactjs |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:57
Часовой пояс: UTC + 5