react-redux 实践代码

2022/01/12 posted in  组件通信
Tags:  #React #实践

效果

src 目录结构

.
├── App.js
├── containers         # 放置容器组件
│   ├── Count
│   │   └── index.jsx
│   └── Person
│       └── index.jsx
├── index.js
└── redux              # 放置 redux 文件
    ├── actions        # 放置 action
    │   ├── count.js
    │   └── person.js
    ├── constant.js    # 定义 action 的 type
    ├── reducers       # 放置 reducer
    │   ├── count.js
    │   ├── index.js   # reducer 汇总
    │   └── person.js
    └── store.js       # store 文件

代码及注释

App.js

import Count from './containers/Count' // 引入 Count 的容器组件
import Person from './containers/Person' // 引入 Person 的容器组件

function App() {
  return (
    <div>
      <Count />
      <hr />
      <Person />
    </div>
  );
}

export default App;

containers/Count/index.jsx

import { Component } from 'react'
// 引入 action
import { increment, incrementAsync } from '../../redux/actions/count'
// 引入 connect 用于连接 UI 组件和 Redux
import { connect } from 'react-redux'

// 定义 UI 组件
class Count extends Component {
    render() {
        const { count, persons, increment, incrementAsync } = this.props
        return (
            <div>
                <h1>我是 Count 组件</h1>
                <h2>我的 count 值:{count},下方 Person 组件的人数:{persons.length}</h2>
                <button onClick={() => increment(1)}>同步 +1</button>&nbsp;
                <button onClick={() => incrementAsync(1, 1000)}>异步 +1</button>
            </div>
        )
    }
}

// 使用 connect()() 创建并暴露一个容器组件
export default connect(
    state => ({
        persons: state.persons,
        count: state.count
    }), // 映射 redux 状态到 UI 组件的属性,redux 状态在汇总的 reducer 中定义
    { increment, incrementAsync } // 映射操作状态的方法
)(Count)

containers/Person/index.jsx

import { Component } from 'react'

import { addPerson } from '../../redux/actions/person'
import { connect } from 'react-redux'

class Person extends Component {
    render() {
        const { count, persons, addPerson } = this.props
        return (
            <div>
                <h1>我是 Person 组件</h1><h2>上方 Count 组件值为:{count}</h2>
                <button onClick={() => addPerson({ id: Math.random(), name: 'tom', age: 18 })}>添加一人</button>
                <ul>
                    {persons.map((person) =>
                        (<li key={person.id}>名字:{person.name} 年龄:{person.age}</li>)
                    )}
                </ul>
            </div>
        )
    }
}

export default connect(
    state => ({ count: state.count, persons: state.persons }),
    { addPerson }
)(Person)

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store'
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    // 此处用 Provider 包裹 App,让 App 所有的后代容器组件都能接收到 store
    <Provider store={store}>
        <App />
    </Provider>
);

redux/actions/count.js

// 此文件专门为 Count 组件生成 action 对象
import { INCREMENT } from '../constant'

// 同步 action,action 值为一般对象
// count 同步增加
export const increment = data => ({ type: INCREMENT, data: data })

// 异步 action,action 值为函数,在函数内可异步派发同步 action
// count 异步增加
export const incrementAsync = (data, time) => {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(increment(data))
        }, time)
    }
}

redux/actions/person.js

import { ADD_PERSON } from '../constant'

// 增加一个人的 action 对象
export const addPerson = personObj => ({ type: ADD_PERSON, data: personObj })

redux/constant.js

// 定义 action 中的 type,目的是为了便于管理和防止写错

export const ADD_PERSON = 'add_person'
export const INCREMENT = 'increment'

redux/reducers/count.js

import { INCREMENT } from '../constant'

// 初始化计数
const initState = 0

export default function countReducer(preState = initState, action) {
    const { type, data } = action
    switch (type) {
        case INCREMENT:
            return preState + data
        default:
            // 初始化时传入的 preState 为 undefined,因此会返回 initState,完成初始化
            // type 不对时,会返回 preState,状态不变
            return preState
    }

}

redux/reducers/index.js

// 此文件用于汇总所有的 reducer 为一个 reducer

// 用于汇总多个 reducer
import { combineReducers } from "redux";
// 为 Count 组件服务的 reducer
import count from './count'
// 为 Person 组件服务的 reducer
import persons from './person'

// 汇总
export default combineReducers({
    count,
    persons
})

redux/reducers/person.js

import { ADD_PERSON } from '../constant'

// 初始化人的列表
const initState = [{ id: '1', name: 'tom', age: 18 }]

export default function personReducer(preState = initState, action) {
    const { type, data } = action
    switch (type) {
        case ADD_PERSON: // 若是添加一个人
            // 返回的对象和原对象不是同一个才会触发重新渲染,所以不能返回原对象
            return [data, ...preState]
        default:
            // 初始化时传入的 preState 为 undefined,因此会返回 initState,完成初始化
            // type 不对时,会返回 preState,状态不变
            return preState
    }

}

redux/store.js

// 引入 createStore 用于创建 store 对象,引入 applyMiddleware 用于添加异步功能
import { createStore, applyMiddleware } from 'redux'
// 引入汇总后的 reducer
import reducer from './reducers'
// 用于支持异步 action
import thunk from 'redux-thunk'
// 用于配合浏览器插件调试 redux
import { composeWithDevTools } from 'redux-devtools-extension'

// 创建并暴露 store
export default createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))