Минималистичное управление состоянием (React) — Блог о самом интересном.

От создателя: 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.

Вам также может понравиться