Что нужно знать о Redux - action/dispatch/reducer/store
2 года назад·3 мин. на чтение
В этой статье рассмотрим работу с Redux - его основные понятия, и как Redux работает с данными.
Redux - это стэйт менеджер для JS приложений. Он может работать не только в React приложениях. Просто так сложилось исторически, что они часто упоминаются вместе.
Redux хранит состояние в дереве объектов внутри единого стора. Единственная возможность изменить состояние - отправить action. Action это объект, который описывает действие. Он как бы отвечает на вопрос "Что я хочу изменить в состоянии?".
Далее action попадает в reducer, где описано как состояние должно быть изменено ("Как я хочу изменить состояние?").
Action’ы добавляют порядок в происходящие изменения. Action - это объект, и его можно залогировать и последовательно отслеживать как меняется состояние.
Подытожив, можно вывести три принципа Redux:
Далее Redux вызывает переданный в него редьюсер. В редьюсер отправляется два аргумента - текущее состояние (
// types.js const ADD_TODO = "ADD_TODO"
// actions.js import { ADD_TODO } from "./types.js" export const addTodoAction = { type: ADD_TODO, payload: { id: 1, text: "Изучить Redux", done: false } }
// todo-reducer.js import { ADD_TODO } from "./types.js" export const todoReducer = (state = [], action) => { switch (action.type) { case ADD_TODO: { const item = action.payload return [...state, item] } default: return state } }
Для чего нужен Redux?
Redux и в целом любая другая система работы с состоянием нужна для контроля над состоянием. Веб приложения усложняются, добавляются новые фичи и контроль над приложением теряется. Становится сложно охватывать большой проект как единое целое. Становится cложно передавать изменения. Встроенными способами в React их приходилось бы передавать по цепочке от одного компонента в другой или через Context API. По дороге обновленное состояние может также влиять на другие компоненты и т.д. И такая бесконтрольность расползается по всему проекту и может послужить появлению новых багов и добавит сложность в отладке. Теряется прозрачность того что происходит.- Единственный источник правды - единый стор, который меняются и всегда содержит актуальные данные.
- Состояние - только для чтения. Нельзя менять его напрямую - только через action’ы.
- Изменения делаются только при помощи чистых функций.
- Эти функции (редьюсеры) принимают в качестве аргумента старое состояние и возвращают обновленное состояние. И всегда при одних и тех же данных результат этих функций будет одинаков.
Понятия в Redux
Action
Action (экшн) - это источник данных в стор, Action включает тип и некоторую информацию (payload). Тип обычно имеет строковое значение. Он нужен чтобы reducer понимал какой action был отправлен. Далее к стору будет применен payload.Reducer
Reducer (редьюсер) - это функция, которая определяет как должно меняться состояние в зависимости от аction’а. Редьюсеры должны быть чистым функциями - они должны вычислить следующее состояние и вернуть новый объект состояния. Никаких сайд эффектов, мутаций состояния, и вызовов API в редьюсере быть не должно.Store
Store - объединяет аction’ы и редьюсеры- Хранит состояние приложения.
- Предоставляет доступ к состоянию через
getState
. - Позволяет изменять состояние через
dispatch
. - Регистрирует подписчиков через
subscribe
.
dispatch
, передав в нее action.
// App.jsx import { useDispatch } from "react-redux" import { ADD_TODO } from "./types.js" export default App() { const dispatch = useDispatch() const handleTodoAdd = () => { dispatch({ type: ADD_TODO, payload: { id: 1, text: "Изучить Redux", done: false }, }) } return ( <button type="button" onClick={handleTodoAdd}>Добавить todo</button> ) }
state
) и action
.
Redux сохраняет весь объект, который вернулся из корневого редьюсера. Почему редьюсер - корневой? В Redux передается один редьюсер, но его можно разделить на несколько и собрать вместе при помощи// todo-reducer.js import { ADD_TODO } from "./types.js" export const todoReducer = (state = [], action) => { switch (action.type) { case ADD_TODO: { const item = action.payload return [...state, item] } default: return state } }
combineReducers
. Результатом этой функции будет корневой редьюсер. Обычно это делается, когда в приложении есть несколько модулей и есть смысл разделить большой редьюсер на несколько маленьких. Но для мелких приложений это совсем не обязательно.
Как уже упоминалось, Redux работает не только с ReactJS. Это универсальный инструмент. Однако для того чтобы использовать его в ReactJS нужен специальный байндинг, который ставится как отдельный пакет -// reducers.js import { combineReducers } from "redux" import { todoReducer } from "./todo-reducer" export const rootReducer = combineReducers({ todos: todoReducer, })
react-redux
.
Как подключить Redux к ReactJS проекту
Установить Redux с помощью команды.Проинициализироватьnpm i redux react-redux
store
и обернуть приложение тегом <Provider>
c переданным в него объектом store
.
// index.js import React from "react" import ReactDOM from "react-dom/client" import { createStore } from "redux" import { Provider } from "react-redux" import App from "./App" import { rootReducer } from "./reducers" const store = createStore(rootReducer) const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> );
20 советов для улучшения качества кода в React проекте
год назад·5 мин. на чтение
React очень гибок и не строг к структуре компонентов. Именно поэтому мы несем ответственность за поддержание чистоты и поддерживаемости наших проектов.
В этой статье рассмотрим некоторые рекомендации по улучшению качества приложения на React. Эти правила широко приняты. Таким образом, обладание этими знаниями является обязательным.
20. Проп
Всегда включайте проп
1. Используйте короткую запись JSX
Попробуйте использовать сокращение JSX для передачи логических переменных. Допустим, вы хотите управлять видимостью заголовка компонента панели навигации.Плохо
return ( <Navbar showTitle={true} /> );
Хорошо
return( <Navbar showTitle /> )
2. Используйте тернарные операторы
Допустим, вы хотите отобразить сведения о пользователе в зависимости от роли.Плохо
const { role } = user; if(role === ADMIN) { return <AdminUser /> }else{ return <NormalUser /> }
Хорошо
const { role } = user; return role === ADMIN ? <AdminUser /> : <NormalUser />
3. Воспользуйтесь преимуществами объектных литералов
Объектные литералы могут помочь сделать наш код более читабельным. Допустим, вы хотите отобразить три типа пользователей в зависимости от их ролей. Вы не можете использовать тернарный оператор, так как количество опций превышает два.Плохо
const {role} = user switch(role){ case ADMIN: return <AdminUser /> case EMPLOYEE: return <EmployeeUser /> case USER: return <NormalUser /> }
Хорошо
Теперь код выглядит намного лучше.const {role} = user const components = { ADMIN: AdminUser, EMPLOYEE: EmployeeUser, USER: NormalUser }; const Component = components[role]; return <Componenent />;
4. Используйте фрагменты
Всегда используйтеFragment
вместо лишней обертки div
. Это сохраняет код чистым, а также полезно для производительности, поскольку в виртуальной модели DOM создается на один узел меньше.
Плохо
return ( <div> <Component1 /> <Component2 /> <Component3 /> </div> )
Хорошо
return ( <> <Component1 /> <Component2 /> <Component3 /> </> )
5. Не определяйте функцию внутри рендеринга
Не определяйте функцию внутри функции рендеринга. Постарайтесь свести логику рендеринга к абсолютному минимуму.Плохо
return ( <button onClick={() => dispatch(ACTION_TO_SEND_DATA)}> // здесь определена функция This is a bad example </button> )
Хорошо
const submitData = () => dispatch(ACTION_TO_SEND_DATA) return ( <button onClick={submitData}> This is a good example </button> )
6. Используйте React memo
React.PureComponent
и Memo
могут значительно повысить производительность вашего приложения. Они помогают нам избежать ненужного рендеринга.
Плохо
Хотя дочерний компонент должен отображаться только один раз, так как значениеimport React, { useState } from "react"; export const TestMemo = () => { const [userName, setUserName] = useState("faisal"); const [count, setCount] = useState(0); const increment = () => setCount((count) => count + 1); return ( <> <ChildrenComponent userName={userName} /> <button onClick={increment}> Increment </button> </> ); }; const ChildrenComponent =({ userName }) => { console.log("rendered", userName); return <div> {userName} </div>; };
count
не имеет ничего общего с ChildComponent
. Но он отображается рендерится каждый раз, когда вы нажимаете на кнопку.
Хорошо
Давайте отредактируемChildrenComponent
следующим образом:
Теперь, независимо от того, сколько раз вы нажмете на кнопку, она будет отображаться только тогда, когда это необходимо.import React, {useState} from "react"; const ChildrenComponent = React.memo(({userName}) => { console.log('rendered') return <div> {userName}</div> })
7. Поместите CSS в JavaScript
Избегайте сырого JavaScript при написании приложений React, потому что организовать CSS намного сложнее, чем организовать JS.Плохо
// CSS FILE .body { height: 10px; } //JSX return <div className='body'> </div>
Хорошо
const bodyStyle = { height: "10px" } return <div style={bodyStyle}> </div>
8. Используйте деструктурирование объектов
Используйте деструктурирование объектов в своих интересах. Допустим, вам нужно показать данные пользователя.Плохо
return ( <> <div> {user.name} </div> <div> {user.age} </div> <div> {user.profession} </div> </> )
Хорошо
const { name, age, profession } = user; return ( <> <div> {name} </div> <div> {age} </div> <div> {profession} </div> </> )
9. Строковые пропсы можно передавать без фигурных скобок
При передаче строковых пропсов дочернему компоненту.Плохо
return( <Navbar title={"My Special App"} /> )
Хорошо
return( <Navbar title="My Special App" /> )
10. Удалить JS-код из JSX
Вынесите JS-код из JSX, если это не служит какой-либо цели рендеринга или функциональности пользовательского интерфейса.Плохо
return ( <ul> {posts.map((post) => ( <li onClick={event => { console.log(event.target, 'clicked!'); // <- плохо }} key={post.id}>{post.title} </li> ))} </ul> );
Хорошо
const onClickHandler = (event) => { console.log(event.target, 'clicked!'); } return ( <ul> {posts.map((post) => ( <li onClick={onClickHandler} key={post.id}>{post.title}</li> ))} </ul> );
11. Используйте шаблонные литералы
Используйте шаблонные литералы для создания длинных строк. Избегайте использования конкатенации строк. Это будет выглядеть красиво и чисто.Плохо
const userDetails = user.name + "'s profession is" + user.proffession return ( <div> {userDetails} </div> )
Хорошо
const userDetails = `${user.name}'s profession is ${user.proffession}` return ( <div> {userDetails} </div> )
12. Порядок импортов
Всегда старайтесь импортировать сущности в определенном порядке. Это улучшает читаемость кода.Плохо
import React from 'react'; import ErrorImg from '../../assets/images/error.png'; import styled from 'styled-components/native'; import colors from '../../styles/colors'; import { PropTypes } from 'prop-types';
Хорошо
Эмпирическое правило состоит в том, чтобы сохранить порядок импорта следующим образом:- Встроенные зависимости
- Внешние зависимости
- Внутренние зависимости
import React from 'react'; import { PropTypes } from 'prop-types'; import styled from 'styled-components/native'; import ErrorImg from '../../assets/images/error.png'; import colors from '../../styles/colors';
13. Используйте неявный возврат
Используйте функцию JavaScript с неявным возвратом результата при написании красивого кода. Допустим, ваша функция выполняет простое вычисление и возвращает результат.Плохо
const add = (a, b) => { return a + b; }
Хорошо
const add = (a, b) => a + b;
14. Именование компонентов
Всегда используйте PascalCase для компонентов и camelCase для экземпляров.Плохо
import reservationCard from './ReservationCard'; const ReservationItem = <ReservationCard />;
Хорошо
import ReservationCard from './ReservationCard'; const reservationItem = <ReservationCard />;
15. Кавычки
Используйте двойные кавычки для атрибутов JSX и одинарные кавычки для всех остальных JS.Плохо
<Foo bar='bar' /> <Foo style={{ left: "20px" }} />
Хорошо
<Foo bar="bar" /> <Foo style={{ left: '20px' }} />
16. Именование пропсов
Всегда используйте camelCase для имен объектов или PascalCase, если значение объекта является компонентом React.Плохо
<Component UserName="hello" phone_number={12345678} />
Хорошо
<MyComponent userName="hello" phoneNumber={12345678} Component={SomeComponent} />
17. JSX в круглых скобках
Если компонент занимает более одной строки, всегда заключайте его в круглые скобки.Плохо
return <MyComponent variant="long"> <MyChild /> </MyComponent>;
Хорошо
return ( <MyComponent variant="long"> <MyChild /> </MyComponent> );
18. Самозакрывающиеся теги
Если компонент не имеет дочерних элементов, используйте самозакрывающиеся теги. Это улучшает читаемость.Плохо
<SomeComponent variant="stuff"></SomeComponent>
Хорошо
<SomeComponent variant="stuff" />
19. Нижнее подчеркивание в названии метода
Не используйте символы подчеркивания ни в одном внутреннем методе React.Плохо
const _onClickHandler = () => { // код }
Хорошо
const onClickHandler = () => { // код }
20. Проп alt
Всегда включайте проп alt
в теги <img >
. И не используйте picture
или image
в проп alt
, потому что программы чтения с экрана уже объявляют элементы img
изображениями.
Плохо
<img src="hello.jpg" /> <img src="hello.jpg" alt="Picture of me rowing a boat" />
Хорошо
<img src="hello.jpg" alt="Me waving hello" />