От создателя: React версии 16.3 представил новейший API контекста. На мой взор, эта новенькая функция довольно хороша для управления состоянием маленьких и средних приложений. Не так давно я написал маленький проект, в каком употреблял контекст в качестве основного источника данных для front-end. В этом посте я желал бы поделиться приобретенными познаниями и подходом.
Новейший API
Давайте быстро вспомним главные моменты.
JavaScript const Context = React.createContext(initialData)
1 | const Context = React.createContext(initialData) |
Делает новейший контекст. Можно иметь несколько контекстов с различными данными.
JavaScript ;
1 | ; |
Воспринимает свойство ‘value’. Будет перерисовывать все связанные пользователи при изменении данных.
JavaScript
Имеет доступ к данным провайдера. Существует два метода доступа пользователя к данным:
1:
JavaScript class Modal extends React.Component { static contextType = AppContext; //… }class Cmp extends Component {render() { console.log(this.context); //…}
}
1234567 | class Modal extends React.Component { static contextType = AppContext; //…}class Cmp extends Component {render() { console.log(this.context); //…}} |
2:
JavaScript render() { return ( {data => {data.title}} )
}
1234567 | render() { return ( {data => {data.title}} )} |
Разница меж новеньким и старенькым API
В древнем API PureComponents и составляющие, которые реализовались shouldComponentUpdate, перерисовывались при изменении характеристики либо состояния. React не учитывает значение контекста. Такое поведение приводит к устареванию данных в контексте.
Вот вам наглядный пример использования старенького API:
JavaScript class App extends Component { static childContextTypes = { counter: PropTypes.object
}
constructor(props) { super(props); this.state = { count: 0, increment: this.increment };
}
increment = () => this.setState({count: this.state.count + 1});
getChildContext() { return { counter: this.state }
}
render() { return ( Counter {this.props.children} ); }
}
class Layout extends Component { render() { return ( ); }
}
%MINIFYHTML89010e855bca7d3fa272ad2dcc3d0dbe35%
class Increment extends React.Component { static contextTypes = { counter: PropTypes.object,
}
render() { return Inctement me }
}
class Title extends React.Component { static contextTypes = { counter: PropTypes.object,
}
render = () =>
{this.context.counter.count}
}
export default function () { return ( );
}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 | class App extends Component { static childContextTypes = { counter: PropTypes.object } constructor(props) { super(props); this.state = { count: 0, increment: this.increment }; } increment = () => this.setState({count: this.state.count + 1}); getChildContext() { return { counter: this.state } } render() { return ( Counter {this.props.children} ); }} class Layout extends Component { render() { return ( ); }} class Increment extends React.Component { static contextTypes = { counter: PropTypes.object, } render() { return Inctement me }} class Title extends React.Component { static contextTypes = { counter: PropTypes.object, } render = () =>
{this.context.counter.count}} export default function () { return ( );} |
Измените компонент Title для расширения PureComponent. Нажмите «Increment» пару раз. не будет перерисован, но значение контекста поменялось.
Минималистичное управление состоянием
Как я уже гласил, я употреблял новейший React Context API для управления состоянием в проекте. Почему я не употреблял redux?
Во-1-х, redux не нужен для маленьких проектов. Просто представьте — деяния, редукторы, резервирование для e2e-связи, connect(), объединение редукторов.
Мне приглянулась мысль сделать приложение, используя лишь React. Сначала все мои данные и средства обновления были в одном файле — Store.
JavaScript class Store extends Component {
/* a lot of methods here
*/
render() { const updaters = {/*methods what*/}; const data = {/* this.state*/}; const value = { data, updaters } return ( {this.props.children} ); }
}
12345678910111213141516171819 | class Store extends Component { /* a lot of methods here */ render() { const updaters = {/*methods what*/}; const data = {/* this.state*/}; const value = { data, updaters } return ( {this.props.children} ); }} |
Таковая реализация покрывала все мои потребности. Но в некий момент я сообразил, что существует наиболее 300 строк кода, чего же полностью довольно.
Пересечение текста линией в CSS
Потому я решил поделить данные зависимо от их типа — Юзер, Сообщения, Продукты, Тема. Я постараюсь, чтоб пример оставался очень обычным. Поначалу я переместил все пользовательские данные из состояния Store в отдельный класс:
JavaScript class User { constructor(getState, rootUpdater) { this._getState = getState;
this._rootUpdater = rootUpdater;
this.name = »; this.surname = »;
}
_setValue = (value = {}) => { this._rootUpdater({ user: { setName: this.setName, setSurname: this.setSurname, …this._getState().user, …value } });
};
setName = (name = ») => { this._setValue({name});
}
setSurname = (surname = ») => { this._setValue({surname});
}
}
1234567891011121314151617181920212223242526272829 | class User { constructor(getState, rootUpdater) { this._getState = getState; this._rootUpdater = rootUpdater; this.name = »; this.surname = »; } _setValue = (value = {}) => { this._rootUpdater({ user: { setName: this.setName, setSurname: this.setSurname, …this._getState().user, …value } }); }; setName = (name = ») => { this._setValue({name}); } setSurname = (surname = ») => { this._setValue({surname}); } } |
Любая модель получает два принципиальных параметра. getState — этот способ возвращает this.state компонента Store. rootUpdater — это способ this.setState из компонента Store. Потом я переработал компонент Store:
JavaScript class Store extends Component { constructor(props) { super(props); this.rootUpdater = (data = {}) => { this.setState({ …this.state, …data })
};
this.getState = () => { return {…this.state};
};
const user = new User(this.getState, this.rootUpdater); this.state = {user};
}
render() { return ( {this.props.children} ); }
}
1234567891011121314151617181920212223242526 | class Store extends Component { constructor(props) { super(props); this.rootUpdater = (data = {}) => { this.setState({ …this.state, …data }) }; this.getState = () => { return {…this.state}; }; const user = new User(this.getState, this.rootUpdater); this.state = {user}; } render() { return ( {this.props.children} ); }} |
Представьте для себя ситуацию, когда одной из моделей нужно выполнить некие вычисления зависимо от значений снутри иной модели. В обход способа this.getState любая модель имеет доступ ко всему дереву данных. Вот полный пример:
JavaScript import React, {Component, PureComponent, createContext} from ‘react’;
const Context = createContext();
class User { constructor(getState, rootUpdater) { this._getState = getState;
this._rootUpdater = rootUpdater;
this.name = »; this.surname = »;
}
_setValue = (value = {}) => { this._rootUpdater({ user: { setName: this.setName, setSurname: this.setSurname, …this._getState().user, …value } });
};
setName = (name = ») => { this._setValue({name});
}
setSurname = (surname = ») => { this._setValue({surname});
}
}
class Store extends Component { constructor(props) { super(props); this.rootUpdater = (data = {}) => { this.setState({ …this.state, …data })
};
this.getState = () => { return this.state;
};
const user = new User(this.getState, this.rootUpdater); this.state = {user};
}
render() { return ( {this.props.children} ); }
}
function Layout() { return ( );
}
class Title extends PureComponent {
static contextType = Context;
render() { return (
name {this.context.user.name}
surname {this.context.user.surname}
); }
}
class Input extends PureComponent { static contextType = Context; constructor(props) { super(props); this.nameInput = React.createRef(); this.surnameInput = React.createRef();
}
setName = (e) => { this.context.user.setName(e.target.value);
}
setSurname = (e) => { this.context.user.setSurname(e.target.value);
}
render() { return (
change name
change name
); }
}
export function App() { return ( );
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 | import React, {Component, PureComponent, createContext} from ‘react’; const Context = createContext(); class User { constructor(getState, rootUpdater) { this._getState = getState; this._rootUpdater = rootUpdater; this.name = »; this.surname = »; } _setValue = (value = {}) => { this._rootUpdater({ user: { setName: this.setName, setSurname: this.setSurname, …this._getState().user, …value } }); }; setName = (name = ») => { this._setValue({name}); } setSurname = (surname = ») => { this._setValue({surname}); } } class Store extends Component { constructor(props) { super(props); this.rootUpdater = (data = {}) => { this.setState({ …this.state, …data }) }; this.getState = () => { return this.state; }; const user = new User(this.getState, this.rootUpdater); this.state = {user}; } render() { return ( {this.props.children} ); }} function Layout() { return ( );} class Title extends PureComponent { static contextType = Context; render() { return (
name {this.context.user.name}
surname {this.context.user.surname}); }} class Input extends PureComponent { static contextType = Context; constructor(props) { super(props); this.nameInput = React.createRef(); this.surnameInput = React.createRef(); } setName = (e) => { this.context.user.setName(e.target.value); } setSurname = (e) => { this.context.user.setSurname(e.target.value); } render() { return ( change name
change name ); }} export function App() { return ( );} |
Помните, что любой вызов this._rootUpdater будет перерисовывать всякого присоединенного пользователя. Задумайтесь о использовании нескольких контекстов, чтоб избежать ненадобных повторных визуализаций.
Создатель: Andrew Palatnyi
Редакция: Команда webformyself.
Вам также может понравиться