メモの内容は、AcWing の Web アプリケーション コースの配布資料 (コース リンク: AcWing Web アプリケーション コース )から転載されています。
前に問題について触れました。つまり、2 つの兄弟コンポーネントが互いのデータにアクセスしたい場合、最も近い共通の祖先にデータを保存する必要がありますが、DOM ツリーが複雑な場合、これは非常に面倒です。Redux は、一部のグローバル値を保存するための DOM ツリー全体の外側の場所です。
1. Reduxの基本概念
Redux はstate
それをツリー構造として維持し、各ノードが値を保存し、関数を使用してreducer
各値を維持します。Redux は辞書を使用して、一般に と呼ばれる子ノードを保存しますstore
。
ツリー内の特定の値を変更する場合は、ツリー全体が再計算されます。dispatch
各関数を再帰的に呼び出す関数を使用しますreducer
。さらに、どのノードを操作する必要があるかを示すオブジェクト パラメータを渡す必要があります。その中には属性があり、type
ノードごとに一意の属性を定義しますtype
。
Redux の基本概念は次のように要約されます。
store
:ストレージツリー構造。state
: 保持されるデータは通常、ツリー構造で保持されます。reducer
:state
更新する関数。state
各 に 1 つバインドされますreducer
。2 つのパラメータ currentstate
とを渡しaction
、 new を返しますstate
。action
reducer
:の受信パラメータを格納し、一般に のstate
更新タイプを記述する共通オブジェクト。type
属性は変更されるノードを表します。dispatch
: パラメータを渡してツリー全体を一度操作action
しstate
ます。つまり、reducer
ツリー全体のすべての関数を再帰的に呼び出します。
まず Redux プロジェクトを作成しましょうredux-app
。
create-react-app redux-app
プロジェクトのルート ディレクトリを入力し、関連モジュールをインストールします。
npm i redux react-redux @reduxjs/toolkit
現在は 1 つだけを維持していると仮定してstate
、最も単純なプレーン バージョンの Redux (React とは関係ありません) を構築します。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import {
configureStore } from '@reduxjs/toolkit';
const f1 = (state = 0, action) => {
// reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const store = configureStore({
// 将f1函数构建成一棵状态树
reducer: f1
});
store.subscribe(() => {
console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
store.dispatch({
type: 'add', val: 2}); // 修改state
store.dispatch({
type: 'add', val: 3});
console.log(store.getState()); // 返回整棵树的值
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
</React.StrictMode>
);
次に、2 つのノードを維持する方法を見てみましょう。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import {
configureStore } from '@reduxjs/toolkit';
const f1 = (state = 0, action) => {
// reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const f2 = (state = '', action) => {
switch(action.type) {
case 'concat':
return state + action.character;
default:
return state;
}
};
const f_all = (state = {
}, action) => {
// 组合了f1与f2
return {
f1: f1(state.f1, action),
f2: f2(state.f2, action),
}
};
const store = configureStore({
// 将f_all函数构建成一棵状态树
reducer: f_all
});
store.subscribe(() => {
console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
store.dispatch({
type: 'add', val: 2}); // 修改f1的state
store.dispatch({
type: 'add', val: 3});
store.dispatch({
type: 'concat', character: 'abc'}); // 修改f2的state
console.log(store.getState()); // 返回整棵树的值
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
</React.StrictMode>
);
コンソール出力は次のとおりです。
{f1: 2, f2: ''}
{f1: 5, f2: ''}
{f1: 5, f2: 'abc'}
{f1: 5, f2: 'abc'}
f_all
自分で記述する必要はなく、以下を使用できますcombineReducers
。
import {
combineReducers } from '@reduxjs/toolkit';
const f_all = combineReducers({
f1: f1,
f2: f2,
});
2. React-Redux の基本概念
次に、Redux と React がどのように組み合わされるかを見てみましょう。Provider
プロジェクト全体を で終える必要があります。React-Redux の基本概念は次のとおりです。
Provider
コンポーネント: プロジェクト全体をラップするために使用され、そのstore
プロパティは Reduxstore
オブジェクトを保存するために使用されます。connect(mapStateToProps, mapDispatchToProps)
関数:store
コンポーネントとの関連付けに使用されます。この関数は関数を返します。返された関数はコンポーネントを入力パラメータとして受け取り、新しいコンポーネントを返すことができます。この新しいコンポーネントは、 の値をコンポーネントのプロパティにバインドしstate
ますprops
。mapStateToProps
:store
のステータスが更新されるたびに 1 回呼び出され、コンポーネントの値が更新されます。つまり、の値store
はstate
コンポーネントのprops
属性にバインドされます。mapDispatchToProps
store
: コンポーネントの作成時に 1 回呼び出され、コンポーネントの関数をコンポーネントに渡すために使用されますdispatch
。つまり、dispatch
関数をコンポーネントのprops
属性にマップします。
表示の便宜上、App
、Number
( state
0 から始まる)、String
(state
空の文字列から始まる) の 3 つのコンポーネントを定義します。
App
コードは以下のように表示されます。
import React, {
Component } from 'react';
import Number from './number';
import String from './string';
class App extends Component {
state = {
}
render() {
return (
<React.Fragment>
<Number />
<hr />
<String />
</React.Fragment>
);
}
}
export default App;
次に、index.js
React-Redux を以下に実装します。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import {
configureStore } from '@reduxjs/toolkit';
import {
combineReducers } from '@reduxjs/toolkit';
import {
Provider } from 'react-redux';
import App from './components/app';
const f1 = (state = 0, action) => {
// reducer函数
switch(action.type) {
case 'add':
return state + action.val;
case 'sub':
return state - action.val;
default:
return state;
}
};
const f2 = (state = '', action) => {
switch(action.type) {
case 'concat':
return state + action.character;
default:
return state;
}
};
const f_all = combineReducers({
number: f1,
string: f2,
});
const store = configureStore({
// 将f_all函数构建成一棵状态树
reducer: f_all
});
store.subscribe(() => {
console.log(store.getState())}); // 每次dispatch完之后会执行一遍该函数
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={
store}>
<App />
</Provider>
);
Number
次に、とコンポーネント の値をString
呼び出す方法を見てみましょうstate
。 API: を使用する必要がありますconnect
。Number
例。
import React, {
Component } from 'react';
import {
connect } from 'react-redux';
class Number extends Component {
state = {
}
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>Number: {
this.props.number}</h3>
</React.Fragment>
);
}
};
const mapStateToProps = (state, props) => {
// 第一个参数state包含整个状态树的树结构
return {
number: state.number,
}
};
export default connect(mapStateToProps)(Number);
state
次に、の値を変更する方法を見てみましょう。にマップするmapDispatchToProps
オブジェクトを定義する必要があります。で操作したいとします。dispatch
props
Number
String
state
import React, {
Component } from 'react';
import {
connect } from 'react-redux';
class Number extends Component {
state = {
}
handleClick = () => {
this.props.concat('abc');
}
render() {
console.log(this.props);
return (
<React.Fragment>
<h3>Number: {
this.props.number}</h3>
<button onClick={
this.handleClick}>添加</button>
</React.Fragment>
);
}
};
const mapStateToProps = (state, props) => {
// 第一个参数state包含整个状态树的树结构
return {
number: state.number,
}
};
const mapDispatchToProps = {
concat: (character) => {
return {
// 会返回一个对象,这个对象就是dispatch中用到的action,会将返回值作用到整个状态树中
type: 'concat',
character: character,
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Number);