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

Минималистичное управление состоянием (React) Сайтостроение

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

От создателя: React версии 16.3 представил новейший API контекста. На мой взор, эта новенькая функция довольно хороша для управления состоянием маленьких и средних приложений. Не так давно я написал маленький проект, в каком употреблял контекст в качестве основного источника данных для front-end. В этом посте я желал бы поделиться приобретенными познаниями и подходом.

Новейший API

Давайте быстро вспомним главные моменты.

JavaScript

1const Context = React.createContext(initialData)

Делает новейший контекст. Можно иметь несколько контекстов с различными данными.

JavaScript

1<Context.Provider value={data}/>;

Воспринимает свойство ‘value’. Будет перерисовывать все связанные пользователи при изменении данных.

JavaScript

1<Context.Consumer/>

Имеет доступ к данным провайдера. Существует два метода доступа пользователя к данным:

1:

JavaScript

1234567class Modal extends React.Component {    static contextType = AppContext;    //…}class Cmp extends Component {render() {     console.log(this.context);     //…}}

2:

JavaScript

1234567render() {  return (      <Consumer>        {data => <div>{data.title}</div>}      </Consumer>   )}

Разница меж новеньким и старенькым API

В древнем API PureComponents и составляющие, которые реализовались shouldComponentUpdate, перерисовывались при изменении характеристики либо состояния. React не учитывает значение контекста. Такое поведение приводит к устареванию данных в контексте.

Вот вам наглядный пример использования старенького API:

JavaScript

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970class 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 (      <div className=”App”>        <header className=”App-header”>          Counter        </header>        {this.props.children}      </div>);  }} class Layout extends Component {  render() {    return (      <div>          <Title />          <Increment/>      </div>    );  }} class Increment extends React.Component {  static contextTypes = {    counter: PropTypes.object,  }   render() {    return <button onClick={this.context.counter.increment}> Inctement me</button>  }}  class Title extends React.Component {  static contextTypes = {    counter: PropTypes.object,  }   render = () => <h1>{this.context.counter.count}</h1>}  export default function () {  return (    <App>      <Layout/>    </App>  );}

Измените компонент Title для расширения PureComponent. Нажмите «Increment» пару раз. <Title/> не будет перерисован, но значение контекста поменялось.

Минималистичное управление состоянием

Как я уже гласил, я употреблял новейший React Context API для управления состоянием в проекте. Почему я не употреблял redux?

Во-1-х, redux не нужен для маленьких проектов. Просто представьте — деяния, редукторы, резервирование для e2e-связи, connect(), объединение редукторов.

Мне приглянулась мысль сделать приложение, используя лишь React. Сначала все мои данные и средства обновления были в одном файле — Store.

JavaScript

12345678910111213141516171819class Store extends Component {   /*    a lot of methods here  */   render() {    const updaters = {/*methods what*/};    const data = {/* this.state*/};    const value = {      data, updaters    }    return (      <AppContext.Provider value={value}>        {this.props.children}      </AppContext.Provider>    );  }}

Таковая реализация покрывала все мои потребности. Но в некий момент я сообразил, что существует наиболее 300 строк кода, чего же полностью довольно.

READ
Ориентация изображений в Веб

Потому я решил поделить данные зависимо от их типа — Юзер, Сообщения, Продукты, Тема. Я постараюсь, чтоб пример оставался очень обычным. Поначалу я переместил все пользовательские данные из состояния Store в отдельный класс:

JavaScript

1234567891011121314151617181920212223242526272829class 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

1234567891011121314151617181920212223242526class 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 (      <Context.Provider value={this.state}>        {this.props.children}      </Context.Provider>    );  }}

Представьте для себя ситуацию, когда одной из моделей нужно выполнить некие вычисления зависимо от значений снутри иной модели. В обход способа this.getState любая модель имеет доступ ко всему дереву данных. Вот полный пример:

JavaScript

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129import 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 (      <Context.Provider value={this.state}>        {this.props.children}      </Context.Provider>    );  }} function Layout() {  return (    <div>      <Title />      <Input />    </div>  );}  class Title extends PureComponent {  static contextType = Context;   render() {    return (      <div>        <h3>          name {this.context.user.name}        </h3>        <h3>          surname {this.context.user.surname}        </h3>      </div>    );  }}  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 (      <div>        <div>           <p>change name </p>          <input ref={this.nameInput} onChange={this.setName} />        </div>        <div>          <p>change name </p>          <input ref={this.surnameInput} onChange={this.setSurname} />        </div>      </div>    );  }} export function App() {  return (    <Store>      <Layout />    </Store>  );}

Помните, что любой вызов this._rootUpdater будет перерисовывать всякого присоединенного пользователя. Задумайтесь о использовании нескольких контекстов, чтоб избежать ненадобных повторных визуализаций.

Создатель: Andrew Palatnyi

Редакция: Команда webformyself.

Комментарии

comments

Оценить статью
Блог о самом интересном.
Добавить комментарий