Привет, друзья!
Итак, разработчики Реакта решили сделать нашу работу с их либой более линейной, направить, так сказать, нас нерадивых на путь наименьшего шанса ошибиться и написать плохой код, что, на мой взгляд, является нашим неотъемлемым правом и способом совершенствоваться и изобретать. Речь идет о всеми любимых методах componentWillReceiveProps и других из той же серии, их больше не будет, но нам дадут альтернативу в виде статического метода getDerivedStateFromProps. Лично мне он напоминает темную комнату, где лежат вещи, и их нужно найти, но ничего не видно.
Разработчики в своих ответах на гневные комментарии пользователей Реакта пишут мол: Ну не дадим мы вам prevProps, это невозможно, придумайте что-нибудь, prevProps нет, ну вы держитесь там, просто кешируйте их в состоянии, в общем предлагают нам сделать небольшой костылек в нашем новом хорошем коде. Это все конечно несложно, можно понять и простить, но вот меня раздосадовал тот факт, что теперь у меня нет контекста this, комнату мою замуровали, из нее ничего не видно, даже соседей не слышно, вот и решил я написать для себя штуку, которая скроет в себе все костыли и мой код будет с виду хоть и странным, но бескостыльным (а бескостыльным ли?).
В общем, мне нужно внедрить prevProps в состояние компонента, еще хочется чтобы все выглядело как обычно, а также невозможно прожить без волшебного this в статическом getDerivedStateFromProps (вот дурак!). Два дня мучений и самосовершенствования и все готово, я родил мышь.
Установка
npm install --save state-master
Использование
Просто пишем такие же getDerivedStateFromProps и componentDidUpdate, но уже модифицированные.
Оборачиваем наш компонент в «withStateMaster», передаем туда список «пропсов», изменения которых нужно отслеживать
import {Component} from 'react' import {withStateMaster, registerContext, unregisterContext} from 'state-master'; // Список "пропсов", изменения которых нужно отслеживать const PROP_LIST = ['width', 'height', 'bgColor', 'fontSize', 'autoSize']; // или просто строка, если только одно значение const PROP_LIST = 'value'; // добавление начального состояния опционально const INITIAL_STATE = { width: 1000, height: 500 }; class ContainerComponent extends Component { static displayName = 'Container'; static getDerivedStateFromProps(data) { const { nextProps, prevProps, state, isInitial, changed, changedProps, isChanged, add, addIfChanged, isChangedAny, addIfChangedAny, isChangedAll, call, get } = data; // ниже пойдет речь об изменившихся пропсах, это только те, которые были указаны в массиве PROPS_LIST // метка о том, что это первый вызов после конструктора if (isInitial) { // добавляем поле "name" с нужным значением "value" к возвращаемому изменению состояния add('name', value); // добавляем поле "name" со значением взятым из пришедших пропсов add('name'); } // changedProps это массив, который содержит имена всех поменявшихся пропсов if (changedProps.indexOf('value') !== -1) { add('value'); } // возвращает true если данный prop как-либо изменился if (isChanged('autoSize')) { add('autoSize'); } // возвращает true если данный prop изменился на указанное значение (здесь на true) if (isChanged('autoSize', true)) { add('autoSize', true); } // changed является true, если один из пропсов как-либо изменился if (changed) { add('somethingChanged', true); } // возвращает true, если один из пропсов как-либо изменился // работает так же, как и пример выше if (isChangedAny()) { add('somethingChanged', true); } // возвращает true, если один из указанных пропсов как-либо изменился if (isChangedAny('bgColor', 'fontSize', ...)) { const {bgColor, fontSize} = nextProps; add('style', {bgColor, fontSize}); } // возвращает true, если все пропсы из списка PROPS_LIST как-либо изменились if (isChangedAll()) { add('allChanged', true); } // возвращает true, если все из указанных пропсов как-либо изменились if (isChangedAll('width', 'height', ...)) { const {width, height} = nextProps; add('size', width + 'x' + height); // вызывает функцию с таймаутом // то же самое, что и setTimeout(() => this.changeSomething(), 0); // используйте для каких-либо действий, которые нужно выполнить по завершению апдейта компонента // хотя правильнее располагать этот вызов в componentDidUpdate call(() => { this.initNewSizes(width, height); }); } // вызывает метод "add", если указанный prop как-либо изменился addIfChanged('name', value); addIfChanged('name'); // вызывает метод "add", если какой-либо prop из списка PROPS_LIST как-либо изменился addIfChangedAny('name', value); addIfChangedAny('name'); // возвращает объект изменения состояния или null // нужно для отладки, чтобы знать, что ушло в состояние // располагайте в конце console.log(get()); // если вы использовали метод "add", то возвращать ничего не нужно // или вы можете просто вернуть объект, как и обычно без всяких вызовов "add" return { size: nextProps.width + 'x' + nextProps.height } } constructor(props) { super(props); // используйте "registerContext", если вам необходим this контекст в getDerivedStateFromProps // если компонент наследуется от другого, в котором был вызван "registerContext", то здесь этого делать не нужно registerContext(this); } // данный метод также будет модифицирован componentDidUpdate(data) { const { prevProps, prevState, snapshot, changedProps, changed, isChanged, isChangedAny, isChangedAll } = data; if (isChanged('value')) { const {value} = this.props; this.doSomeAction(value); } } componentWillUnmount() { // также добавляйте этот код, если "registerContext" был вызван в конструкторе unregisterContext(this); } render() { const {style, size} = this.state; return ( <div className="container" style={style}> Size is {size} </div> ) } } export const Container = withStateMaster(ContainerComponent, PROP_LIST, INITIAL_STATE);
Если компонент наследуется от другого, передайте родителя, чтобы родительский getDerivedStateFromProps был вызван
export const Container = withStateMaster(ContainerComponent, PROP_LIST, null, ParentalComponent);
Такого мое решение данной проблемы (хотя возможно я недостаточно понял реакт, и это вовсе не проблема)
Таким образом я вступил в сопротивление новым канонам Реакта, возможно когда-нибудь я смирюсь и перепишу все как надо.
Хотя разработчики возможно опять все переделают и возникнут другие насущные вопросы.
Всё, я ложусь, а лежачих, как говорится, не бьют))
ссылка на оригинал статьи https://habr.com/post/421615/
Добавить комментарий