Знаниями нужно делится...

React - общее состояние

Назад к списку | Просмотров: 18

В этой статье речь пойдет об обмене состояниями между компонентами в программах написанных на React Js.

Для начала нужно создать простой файл HTML и добавить три скрипта (конечно такой подход неприемлем в выпущенном приложении, где нужно использовать некий сборщик. Но в нашем тренировочном варианте вполне сгодиться и так. )

			
			class App extends React.Component {

				constructor(props) {
					super(props);

					this.state = {a: 0, b: 0};
					this.onChangeHandler = this.onChangeHandler.bind(this);
				}

				onChangeHandler (e)  {
					let newState = {};
					newState[e.target.name] = parseInt(e.target.value);
					this.setState(newState);
				}	


				render() {
					return <div>
						<h1>Hello world!</h1>
						<div>
							<label>A: <input name="a" value={ this.state.a} onChange={this.onChangeHandler}/>
							</label>

							<label>B: <input name="b" value={ this.state.b} onChange={this.onChangeHandler}/>
							</label>

							<span>= { this.state.a +  this.state.b}</span>
						</div>
					</div>
				}
			}

			ReactDOM.render(
                <App/>
                , 
			document.getElementById('root')); 

		

Итак по порядку для тех кто совсем не в теме

react.min.js - библиотека самого React
react-dom.min.js - библиотека для работы React c DOM
babel-core/.../browser.min.js - Babel процессор для того чтобы наш браузер понимал различные отклонения от чистого JavaScript

Далее идет простой пример приложения App которое выводит текст и складывает два числа из формы a, b - пишет результат. Вроде все хорошо и беспокоится не о чем. Но что если наше приложение разрастется - а как показывает практика оно всегда разрастается и что бы держать его в удобоваримом виде его нужно как то оптимизировать. Самое простое разбивать на более мелкие части. Скажем мы можем сделать отдельный класс который будет позволять нам вводить числа, так же можно вынести модель данных в отдельный класс.

И на самом деле разделение приложения на небольшие куски хорошая практика, но проблема в том что как теперь заставить отдельные компоненты обмениваться данными - состояниями. По сути автоматическое обновление данных через состояние в компоненте реакта и есть его основная фишка, а при разбиении компонентов мы теряем этот функционал!

На самом деле это проблема решается с помощью старого доброго потерна "Слушатель" (Observer). И главное реализовать его будет совсем несложно.


			class InputInt extends React.Component {

				constructor(props) {
					super(props);

					this.state = {value: props.value || 0};
					this.onChangeHandler = this.onChangeHandler.bind(this);
				}

				onChangeHandler(e) {
					this.setState({
						value: parseInt(e.target.value, 10)
					});

					var event = new CustomEvent('InputInt.update', {detail: e.target });	
					window.dispatchEvent( event );
				}

				render() {

					return <label>
						{this.props.label}: 
						<input name={this.props.inputName} value={ this.state.value} onChange={this.onChangeHandler}/>
					</label>;
				}
			}
			
			class App1 extends React.Component {

				constructor(props) {
					super(props);

					this.state = {a: 0, b: 0}; 
					window.addEventListener('InputInt.update', (e) => {
						var target =  e.detail,
							newState = {}
						; 
						newState[target.name] = parseInt(target.value, 10);
						this.setState(newState);
					});
				}
 

				render() {
					return <div> 
						<div>
							<InputInt inputName="a" label="A"/>

							<InputInt inputName="b" label="B"/>

							<span>= { this.state.a +  this.state.b}</span>
						</div>
					</div>
				}
			}

			ReactDOM.render(
                <App1/>
                , 
			document.getElementById('root1')); 

		

Но как не всегда удобно реализовывать эту логику с нуля. По этому как говорится "Шеф все уже украдено до нас...". Есть замечательная библиотека mobx-react которая делает все за нас... то есть создает общее состояние и позволяет слушать изменения внутри него нашим компонентам.


<script src="https://unpkg.com/mobx@3.2.2/lib/mobx.umd.js"></script>
<script src="https://unpkg.com/mobx-react@4.2.2"></script>


 

				

			class InputInt extends React.Component {

				constructor(props) {
					super(props);
 
					this.onChangeHandler = this.onChangeHandler.bind(this);
				}

				onChangeHandler(e) {
				 

					this.props.store[e.target.name] = parseInt(e.target.value, 10); 
				}

				render() {
					const name = this.props.inputName;
					return <label>
						{this.props.label}: 
						<input name={name} value={ this.props.store[name]} onChange={this.onChangeHandler}/>
					</label>;
				}
			}
			
			 
			class App2 extends React.Component { 

				render() {
					return <div> 
						<div>
							<InputInt inputName="a" label="A" store={store}/>

							<InputInt inputName="b" label="B" store={store}/>

							<span>= { this.props.store.a +  this.props.store.b}</span>
						</div>
					</div>
				}
			}

			//создаем общий контекст
			const store = mobx.observable({a: 0, b: 0});

			//делаем из App2 слушателя, который будет получать уведомления при изменении контекста
			App2 = mobxReact.observer(App2);

			ReactDOM.render( 
               	 <App2 store={store}/>
                , 
			document.getElementById('root2')); 

		

Разумеется приведенные примеры не являются единственными. Существуют и другие библиотеки с аналогичным функционалом. Так же есть возможность использовать такие конструкции как ref= - т.е. получение компонента по ссылке (Официально React не рекомендует этот способ). Важно понять что все это работает на событийной модели и в основе множества новых библиотек стоят старые добрые потерны проектирования. Надеюсь моя статья была полезной.


автор admin дата 02/09/2017


Оставить комментарий
5 + 9 =