Эта статья является продолжением моей предыдущей статьи. Если вы новичок в крючках и приехали прямо сюда, то я бы посоветовал вам сначала проверить эту первую статью, а затем прийти сюда. Таким образом, вы сможете ясно понять все в этой статье.
Если вы знакомы с основами хуков в React, вроде useEffect
или useState
, то вам, вероятно, хорошо идти.
API перехвата Redux предоставляют альтернативу для подключения HOC и отмены, mapStateToProps
и mapDispatchToProps
, я должен сказать, API перехвата Redux обеспечивает более чистый способ сделать это.
Теперь, не вдаваясь в теорию, давайте углубимся в некоторый код. Для начала давайте создадим новый файл компонента src / components / ReduxHooksComponent.tsx с двумя полями ввода и кнопкой отправки.
Создать нормальный функциональный компонент
Машинопись
1
import React, {ChangeEvent, FormEvent, useState, useEffect} from "react";
2
import {Form, Input, Button} from "antd";
3
4
interface Props {
5
}
6
7
const ReduxHooksComponent: React.FC<Props> = () => {
8
9
10
return (
11
<Form layout="inline">
12
<Form.Item>
13
<Input type="text" placeholder="name"/>
14
<Input type="text" placeholder="address" />
15
<Button htmlType="submit" type="primary"> Submit </Button>
16
</Form.Item>
17
</Form>
18
)
19
};
20
21
export default ReduxHooksComponent;
Теперь импортируйте этот компонент в App.tsx .
Машинопись
xxxxxxxxxx
1
import React from 'react';
2
import './App.css';
3
import ReduxHooksComponent from "./components/ReduxHooksComponent";
4
5
const App: React.FC = () => {
6
return (
7
<div className="App">
8
<ReduxHooksComponent></ReduxHooksComponent>
9
</div>
10
);
11
};
12
13
export default App;
Довольно просто, правда? После запуска кода он должен отобразить компонент с двумя входами и кнопкой отправки.
Настройка магазина, действий и редукторов
Сначала добавим redux
и react-redux
в проект.
Машинопись
xxxxxxxxxx
1
yarn add react-redux @types/react-redux redux
Создайте два файла: src / store / index.ts и src / store / root-reducer.ts.
Давайте начнем создавать каждый компонент корневого редуктора (действия, состояния и редукторы).
Машинопись
xxxxxxxxxx
1
# src/store/root-reducer.ts
2
3
import {Action, Reducer} from "redux";
4
5
export interface InitialState {
6
name: string;
7
address: string;
8
}
9
10
export const initialState: InitialState = {
11
name: '',
12
address: '',
13
};
14
15
export interface DispatchAction extends Action {
16
payload: Partial<InitialState>;
17
}
18
19
export const rootReducer: Reducer<InitialState, DispatchAction> = (state, action) => {
20
return initialState;
21
};
Теперь у нас есть простой редуктор, который ничего не делает, кроме как возвращает исходное состояние. Давайте создадим магазин, используя это rootReducer
, поэтому наш src / store / index.ts будет выглядеть так:
Машинопись
xxxxxxxxxx
1
import {DispatchAction, InitialState, rootReducer} from "./root-reducer";
2
import {createStore} from "redux";
3
export const store = createStore<InitialState, DispatchAction, null, null>(rootReducer);
Кроме того, мы обновим файл index.tsx, чтобы обернуть наше приложение с провайдером и предоставим хранилище провайдеру.
Машинопись
xxxxxxxxxx
1
# src/index.tsx
2
3
..
4
ReactDOM.render(<Provider store={store}><App ></App></Provider>, document.getElementById('root'));
5
..
Это все. Ну, вы не увидите никаких видимых изменений в браузере, но в итоге вы успешно интегрировали Redux в свой код React.
Теперь давайте создадим некоторые действия. Обновите файл root-reducer.ts, добавив следующий код:
Машинопись
xxxxxxxxxx
1
# src/store/root-reducer.ts
2
3
.
4
export interface DispatchAction extends Action<ActionType> {
5
payload: Partial<InitialState>;
6
}
7
8
export enum ActionType {
9
UpdateName,
10
UpdateAddress,
11
DeleteName,
12
DeleteAddress,
13
}
14
15
export const rootReducer: Reducer<InitialState, DispatchAction> = (state = initialState, action) => {
16
if (action.type === ActionType.UpdateName) {
17
return {state, name: action.payload.name || ''};
18
} else if (action.type === ActionType.DeleteName) {
19
return {state, name: ''};
20
} else if (action.type === ActionType.DeleteAddress) {
21
return {state, address: ''};
22
} else if (action.type === ActionType.UpdateAddress) {
23
return {state, name: action.payload.name || ''};
24
} else return state;
25
};
Довольно просто, да! Мы только что вернули обновленную версию состояния с новыми значениями согласно предложениям действий.
Теперь давайте создадим Dispatcher в том же файле.
Машинопись
xxxxxxxxxx
1
src/store/root-redux.ts
2
3
.
4
export class RootDispatcher {
5
6
private readonly dispatch: Dispatch<DispatchAction>;
7
8
constructor(dispatch: Dispatch<DispatchAction>){
9
this.dispatch = dispatch;
10
}
11
updateName = (name: string) => this.dispatch({type: ActionType.UpdateName, payload: {name}});
12
13
updateAddress = (address: string) => this.dispatch({type: ActionType.UpdateAddress, payload: {address}});
14
15
deleteName = () => this.dispatch({type: ActionType.DeleteName, payload: {}});
16
17
deleteAddress = () => this.dispatch({type: ActionType.DeleteAddress, payload: {}})
18
}
19
Мы закончили со всеми диспетчерами, действиями и хранилищем (в основном, всей нашей установкой Redux). Все, что нам нужно сделать, это отправить действия от компонентов и использовать значения из хранилища.
Использование Dispatcher и сохранение в компонентах через хуки
Крюки Redux предоставляют два основных крючка, useSelector
и useDispatch
.
useSelector
дает доступ ко всему объекту магазина, и мы можем выбрать только то, что нам интересно.
useDispatch
предоставить доступ к диспетчеризации, которая будет использоваться для создания Dispatcher
объекта, RootDispatcher
а затем отправлять события для обновления состояния.
Давайте создадим интерфейс со всеми интересующими нас свойствами из магазина:
Машинопись
xxxxxxxxxx
1
interface StateProps {
2
name: string;
3
address: string;
4
}
Когда можно использовать UseSelector
для назначения имени и адреса.
Машинопись
xxxxxxxxxx
1
const {name, address} = useSelector<InitialState, StateProps>((state: InitialState) => {
2
return {
3
name: state.name,
4
address: state.address
5
}
6
});
Теперь мы можем использовать
useDispatch
объект диспетчеризации и создать новый экземплярRootDispatcher
.Машинопись
xxxxxxxxxx
1
const dispatch = useDispatch();
2
const rootDispatcher = new RootDispatcher(dispatch);
После интеграции и компонента, и диспетчера в компонент наш файл будет выглядеть примерно так:
Машинопись
xxxxxxxxxx
1
#src/components/ReduxHooksComponent.tsx
2
3
import React, {ChangeEvent, FormEvent, useState, useEffect} from "react";
4
import {Form, Input, Button} from "antd";
5
import {useDispatch, useSelector} from "react-redux";
6
import {InitialState, RootDispatcher} from "../store/root-reducer";
7
8
interface Props {
9
}
10
11
interface StateProps {
12
name: string;
13
address: string;
14
}
15
16
const ReduxHooksComponent: React.FC<Props> = () => {
17
18
const {name, address} = useSelector<InitialState, StateProps>((state: InitialState) => {
19
return {
20
name: state.name,
21
address: state.address
22
}
23
});
24
25
const dispatch = useDispatch();
26
const rootDispatcher = new RootDispatcher(dispatch);
27
28
29
return (
30
<Form layout="inline">
31
<Form.Item>
32
<Input type="text" placeholder="name" value={name}
33
onChange={(e: ChangeEvent<HTMLInputElement>) => {
34
rootDispatcher.updateName(e.target.value)}
35
}
36
/>
37
<Input type="text" placeholder="address" value={address}
38
onChange={(e: ChangeEvent<HTMLInputElement>) =>{
39
rootDispatcher.updateAddress(e.target.value)}
40
}
41
/>
42
<Button htmlType="submit" type="primary"> Submit </Button>
43
</Form.Item>
44
</Form>
45
)
46
};
47
48
export default ReduxHooksComponent;
mapStateToProps
аргументуconnect
концептуально. Селектор будет вызываться со всем состоянием хранилища Redux в качестве единственного аргумента. Селектор будет запускаться всякий раз, когда компонент функции рендерится.useSelector()
также будет подписываться на магазин Redux и запускать ваш селектор всякий раз, когда отправляется действие.по умолчанию
useSelector()
используются строгие===
проверки на равенство ссылок, а не поверхностное равенство. Чтобы использовать неглубокое равенство, мы можем использоватьshallowEqual
от реагировать-редуксаИтак, наш код селектора будет выглядеть примерно так:
Машинопись
xxxxxxxxxx
1
const {name, address} = useSelector<InitialState, StateProps>((state: InitialState) => {
2
return {
3
name: state.name,
4
address: state.address
5
}
6
}, shallowEqual);