когда нечего делать, а творческая душа требует реализации и хочется что-то сделать или написать, то я пишу сюда или делаю что-то на этом сайте. Кому интересна web-разработка, wordpress и то что рядом с этим, то заходите на мой сайт и читайте всякое...
React getDerivedStateFromProps. Создание удобных интерфейсов
Йо-йо! Пару месяцев назад (сентябрь 2019) я переехал в МСК и было не до блога. Но теперь я вроде разобрался с самым важным и готов вновь быть проводником знаний для своих читателей. И так…
На новой работе я недавно писал react-компонент для подписки и добавления автомобиля в избранное.
Кнопки действий я подчеркнул
По факту, когда пользователь нажимает на кнопку идёт запрос на сервер, сервер отвечает и тогда пользователь подписался или добавил авто в избранное.
В прошлой реализации списка автомобилей иконки подсвечивались только после ответа сервера и пока ожидался ответ создавалось впечатление, что пользователь промахнулся или каталог тормозит. Это явно мне не нравилось и я решил отображать сразу же новое состояние и просто отправлять данные на сервер.
Проблемы
1. Проблема заключалась в том, что данные я получал из redux довольно специфическим методом т.к. структура данных списка довольно непростая (ввиду разных причин) и из-за этого я не хотел, изменять состояние в store сразу
2. Обновление store могло случиться не только в каталоге, но и в отдельном компоненте со списком избранного.
Открывается в боковой колонке
Т.к. одновременно не видно избранное и каталог, данные в избранном можно обновить после ответа сервера, т.е. после изменения props
Решение и getDerivedStateFromProps
Схема работы у меня получилась такая: При клике на подписку (например) я сразу меняю цвет, отправляю запрос на сервер и вызываю dispatch в redux после ответа. Если ответ будет отрицательным то я получу другие данные и отменю действие по добавлению.
Как же это в коде…. я умышленно пропущу часть из redux. В данном случае он не важен. А ещё вы можете по-разному «отыграть» ситуацию после ответа (показать сообщение об удачной или неудачной подписке например).
class AddToFavorites extends Component {
constructor(props) {
super(props);
const { subscribe, data } = this.props;
this.state = {
data: data,
subscribe: subscribe
}
}
handelSubscribe = () => {
const { subscribeAction, data, subscribe } = this.props;
this.setState({ subscribe: !subscribe }); /* Изменяю state */
subscribeAction(data); /* Отправляю данные в Redux */
}
/* Прочий код */
render() {
const { subscribe } = this.state;
{/* Прочий код */}
return (
<Fragment>
<span className={pr10 listItem__addButtons ${this.props.className}}>
{/* Прочий код */}
<svg
onClick={handelSubscribe}
className={favorite_iconbox__icon mr-right10 ${ (subscribe) ? 'active' : '' }}
>
<use xlinkHref={#notification}>
<title>Подписаться на изменение цены</title>
</use>
</svg>
{/* Прочий код */}
</span>
</Fragment>
)
}
}
Почему я вообще сохраняю состояние подписан/не подписан в state?! Т.к. пользователь в любой момент может, например отфильтровать список или сделать другое действие, которое может вызвать перерендер, компонент бы обязательно обратился к store в котором ещё нет нового значения.
Далее, мы допишем код так, чтобы знать предыдущее состояние пришедшее к нам из пропс.
constructor(props) {
super(props);
const { subscribe, data } = this.props;
this.state = {
memoryProps: this.props, // Вот тут
data: data,
subscribe: subscribe
}
}
Так мы сможем сравнивать изменилось ли значение props пришедшее ранее (как в старом componentWillUpdate). Собственно давайте сравним. Дополним наш компонент методом
Мы сравниваем новое и старое значение состояния подписки и если оно измениться то и поменяем его в state. Так мы не даём изменить состояние при других действиях пользователя.
Такого же эффекта можно было бы добиться и с помощью PureComponent. Но он бы был уместен если у вас была только одна функция (подписка, например), а не как у меня несколько.
Итог getDerivedStateFromProps
Его использование помогло повысить usability. Для лучшего UX вы бы могли бы выводить не навязчивое окно с результатом (положительная подписка или отрицательная). Таким образом мы улучшаем UX и поддерживаем асинхронность. Надеюсь вам будет полезен приведённый пример.
P.S.
Такое вы можете провернуть не только с redux, но и при использовании данных из hight order component.
Поддержи Xakplant
Я давно хочу развить видеоверсию, но пока этого не получается из-за нехватки ресурсов. Сейчас я собираю деньги на новый компьютер и микрофон. Поддержи xaklant и ты увидишь полезные видео быстрее.