React+Redux или как написать свой сервис (Часть 2: Продолжение)

Разработка приложения под текущие задачи – сложный, но перспективный процесс. Сегодня расскажем как создать структуру приложения пошагово и на практическом примере.

Привет! В предыдущей статье (ссылка) мы с вами заложили первый кирпичик для разработки нашего сервиса. Теперь давайте создадим структуру приложения.

В папке src создадим папки actions, components, reducers, services и файл store.js. Последний нам понадобится для использования redux.

В папке components мы будем размещать все компоненты нашего сервиса. Создадим там папку app и в неё перенесем следующие файлы:

  1. app.js
  2. app.css

В файле app.js будет размещаться код компонента, а в app.css расположатся стили компонента.

Также нам понадобится index.js. Он нужен для удобства импорта этого компонента, и будет содержать всего две строки:

import App from './app'; export default App;

Теперь в файле index.js в папке src мы изменим строку импорта компонента App.

import App from ‘./app’;

меняем на

import App from './components/app';

Теперь попробуем запустить проект, чтобы посмотреть не сделали ли мы ошибку.

Необходимо запустить командную строку. Перейти в папку нашего проекта и ввести команду npmstart.

Должна открыться пустая страница проекта.

Теперь добавим нашему элементу функциональности. Добавим следующий код в компонент App внутрь тега div:

<div> <label>Имя: <input type='text' /></label> </div> <div style={{ width: '10%' }}> <label>Сила: <input type='number' /></label> <label>Ловкость: <input type='number' /></label> <label>Телосложение: <input type='number' /></label> <label>Интеллект: <input type='number' /></label> <label>Мудрость: <input type='number' /></label> <label>Харизма: <input type='number' /></label> </div>

У нас появились поля ввода характеристик для нашего героя.

Чтобы хранить значения, нам необходимо немного изменить элемент, сделав его не функцией, а классом. Для этого импортируем из библиотеки react Component:

import React, { Component } from 'react';

Затем модифицируем элемент:

export default class App extends Component {...}

И удалим следующую строку:

export default App;

Отлично! Теперь App подготовлен для дальнейших манипуляций.

Создадим state, в котором будет храниться состояние объекта. Внутри нашего класса пишем следующее:

state = { name: '', strength: 0, dexterity: 0, constitution: 0, intelligence: 0, wisdom: 0, charisma: 0 }

Напишем обработчик событий для полей. Он располагается тоже внутри класса:

onChangeInput(changeInput, ev) { const val = ev.target.value; this.setState((state) => { return { ...state, [changeInput]: val } }) }

ev – это событие, которое мы получим от input. changeInput – имя поля, значение которого мы изменяем. setState – функция, которая изменяет state. Так как менять состояние напрямую нельзя, мы возвращаем копию состояния (которую получаем с помощью spread-оператора) и новое значение поля.

В каждом поле ввода перед закрывающим слешем добавим:

onChange={(ev) => this.onChangeInput('name', ev)} value={this.state.name }

onChange — обработчик события будет вызывать функцию onChangeInput, и передавать ей само событие и имя поля которое изменяем в state.

для поля strength в state мы передаем ‘strength’и т.д.

Подсказка ↑

Теперь, при любом изменении любого поля изменения попадают в наш локальный state.

Давайте проверим, что все именно так. Используем метод жизненного цикла компонента componentDidUpdate(). Данный метод вызывается, когда компонент должен обновиться.

Напишем его внутри нашего класса:

componentDidUpdate() { console.log(this.state) }

Открываем браузер и видим, что при каждом изменении любого поля в консоль выводится текущее состояние state.

Теперь приступим к установке библиотек для redux. Переходим в командную строку нажимаем Ctrl+C, Y и Enter.

Пишем следующие строки. После каждой нажимаем Enter и ждем завершения установки:

  1. npm install redux
  2. npm install react-redux
  3. npm install redux-thunk

Теперь у нас установлены все библиотеки для redux, которые понадобятся в этом проекте.

Немножко теории — как работает связка react+redux.

У нашего приложения есть только один источник данных – это store. Store содержит в себе общий state, изменение которого происходит через reducer’ы. Например, если пользователь вводит что-нибудь в наше поле ввода, функция onChange вызывает reducer, который меняет общий state и компонент перерисовывается в связи с изменением state.

Теперь напишем простой reducer, который будет получать имя поля, и значение, которое ввел пользователь.

В файле reducers введем следующее

const initialState = { name: '', strength: 0, dexterity: 0, constitution: 0, intelligence: 0, wisdom: 0, charisma: 0 } const reducer = (state = initialState, action) => { switch (action.type) { case 'CHANGE_PARAMS': { return { ...state, [action.payload.type]: action.payload.value } } } }

initialState – начальное состояние нашего приложения, reducer – чистая функция, которая принимает state и action и возвращает новый state.

В action у нас содержится type – это тип нашего действия, в payload содержится значение, которое мы добавляем в наш state.

Перейдем в папку actions – там будут action-creators которые будут создавать объекты, которые мы будем передавать в reducers.

Создадим файл index.js и напишем следующее:

const changeParams = (type, value) => { return { type: 'CHANGE_PARAMS', payload: { type, value } } } export { changeParams };

Давайте вынесем нашу форму в отдельный компонент Form. Для этого в папке components создадим новую папку form и в ней разместим файлы form.js и index.js

В index.js пишем

import Form from './form' export default Form;

В файл form.js перенесем все, что относится к нашему компоненту, а также импортируем библиотеку react.

import React, { Component } from 'react'; export default class Form extends Component { componentDidUpdate() { console.log(this.state) } onChangeInput(changeInput, ev) { const val = ev.target.value; this.setState((state) => { return { ...state, [changeInput]: val } }) } render() { return ( <div> <div> <label>Имя: <input type='text' value={this.state.name} onChange={(ev) => this.state.onChangeInput('name', ev)} /></label> </div> <div style={{ width: '10%' }}> <label>Сила: <input type='number' value={this.state.strength} onChange={(ev) => this.state.onChangeInput('strength', ev)} /></label> <label>Ловкость: <input type='number' value={this.state.dexterity} onChange={(ev) => this.state.onChangeInput('dexterity', ev)} /></label> <label>Телосложение: <input type='number' value={this.state.constitution} onChange={(ev) => this.state.onChangeInput('constitution', ev)} /></label> <label>Интеллект: <input type='number' value={this.state.intelligence} onChange={(ev) => this.state.onChangeInput('intelligence', ev)} /></label> <label>Мудрость: <input type='number' value={this.state.wisdom} onChange={(ev) => this.state.onChangeInput('wisdom', ev)} /></label> <label>Харизма: <input type='number' value={this.state.charisma} onChange={(ev) => this.state.onChangeInput('charisma', ev)} /></label> </div> </div> ) } }

State в данном компоненте нам больше не нужен – мы будем получать его из store.

Теперь создадим наш store.

Переходим в папку src, открываем файл store.js и пишем следующее

import { createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; import reducer from './reducers'; const store = createStore(reducer, applyMiddleware(thunkMiddleware)); export default store;

Вернемся в файл app.js и импортируем пару необходимых библиотек:

import { Provider } from 'react-redux'; import store from '../../store';

а также Form из form.js и store из store.js:

import Form from '../form' import store from '../../store';

Обернем элемент Form в Provider, а в качестве параметра передадим в Provider store. В итоге элемент app.js будет выглядеть следующим образом.

import React, { Component } from 'react'; import { Provider } from 'react-redux'; import Form from '../form' import store from '../../store'; import './app.css'; export default class App extends Component { render() { return ( <div> <Provider store={store}> <Form/> </Provider> </div> ); }; }

В файле form.js необходимо подключить store, который provider передает всем компонентам внутри себя.

Импортируем функцию connect из react-redux и changeParams из actions.

import { connect } from 'react-redux'; import { changeParams } from '../../actions'

Так как мы будем применять функцию connect к нашему компоненту уберем export default из объявления класса и перенесем данный export вниз.

export default Form;

Добавим функцию connect для подключения store. Данная функция принимает в себя две функции mapStateToProps и mapDispatchToProps и выполняется относительно Form. Таким образом наш export примет вид

export default connect(mapStateToProps, mapDispatchToProps)(Form);

С помощью mapStateToProps мы получаем значение store, а с помощью mapDispatchToProps получаем возможность на него воздействовать через action-creator. Давайте напишем эти функции.

const mapStateToProps = (state) => { return { params: state } } const mapDispatchToProps = (dispatch) => { return { changeParams: (nameField, type) => { dispatch(changeParams(nameField, type)) } } }

Удаляем функцию onChangeInput – она нам больше не нужна.

Во всех полях и в функции componentDidUpdate заменим this.state на this.props.params.

А также заменим функцию onChangeInput в input на нашу функцию из store – changeParams.

Должен получиться вот такой компонент:

import React, { Component } from 'react'; import { connect } from 'react-redux'; import { changeParams } from '../../actions' class Form extends Component { componentDidUpdate() { console.log(this.props.params) } render() { return ( <div> <div> <label>Имя: <input type='text' value={this.props.params.name} onChange={(ev) => this.props.changeParams('name', ev.target.value)} /></label> </div> <div style={{ width: '10%' }}> <label>Сила: <input type='number' value={this.props.params.strength} onChange={(ev) => this.props.changeParams('strength', ev.target.value)} /></label> <label>Ловкость: <input type='number' value={this.props.params.dexterity} onChange={(ev) => this.props.changeParams('dexterity', ev.target.value)} /></label> <label>Телосложение: <input type='number' value={this.props.params.constitution} onChange={(ev) => this.props.changeParams('constitution', ev.target.value)} /></label> <label>Интеллект: <input type='number' value={this.props.params.intelligence} onChange={(ev) => this.props.changeParams('intelligence', ev.target.value)} /></label> <label>Мудрость: <input type='number' value={this.props.params.wisdom} onChange={(ev) => this.props.changeParams('wisdom', ev.target.value)} /></label> <label>Харизма: <input type='number' value={this.props.params.charisma} onChange={(ev) => this.props.changeParams('charisma', ev.target.value)} /></label> </div> </div> ) } } const mapStateToProps = (state) => { return { params: state } } const mapDispatchToProps = (dispatch) => { return { changeParams: (nameField, type) => { dispatch(changeParams(nameField, type)) } } } export default connect(mapStateToProps, mapDispatchToProps)(Form);

Теперь, если мы все сделали правильно у нас должно получиться следующее.

Поздравляю, наш минимальный react-redux проект создан. В следующей статье мы попробуем навесить стили и подключим сервис к таблицам для сохранения наших данных.

0
Комментарии

Комментарий удален модератором

Развернуть ветку
-3 комментариев
Раскрывать всегда