react笔记-04redux篇

redux和react-redux笔记

一、redux

1. redux工作流程

在这里插入图片描述

流程:创建action => dispatch分发action => 交给store => reducer加工数据返回给store

2. redux的三个核心概念

2.1 action

动作对象,包含两个属性:

  • type:标识属性,值为string,唯一,必要属性
  • data:数据属性,值类型任意,可选属性
{ type: 'userInfo', data: { name: 'xiaotian', age: 20 } }
2.2 reducer

用于初始化状态、加工状态。加工时,根据旧的stateaction,产生新的state的纯函数。redux的reducer函数必须是一个纯函数。

纯函数:同样的输入,同样的输出

遵循:

  1. 不得改写参数数据
  2. 不会产生任何副作用,例如:网络请求,输入输出设备
  3. 不能调用Date.now()或者Math.random()等不纯的方法

例如:下面这个函数就不是纯函数

// 传入同样的参数num,每次返回的结果都不相同,所以不是纯函数。
function fn(num) {
    return Math.random() + num
}

错误写法:

let personList = []
export default function personReducer(preState = personList, action) {
    const { type, data } = action
    switch (type) {
        case 'AddPerson':
        		// 不能这样写,会导致personList被改写了,personReducer就不是一个纯函数了,会导致reducx不能识别到数据的改变。
            personList.unshift(data)
            return personList
        default:
            return preState
    }
}

正确写法:

let personList = []
export default function countReducer(preState = personList, action) {
    const { type, data } = action
    switch (type) {
        case 'AddPerson':
            return [data, ...personList]
        default:
            return preState
    }
}
2.3 store

用于存储数据,有如下方法:

  • getState(): 获取当前的state对象。

  • dispatch(action): 分发action,这是改变state的唯一途径。每个action是一个描述“发生了什么”的普通JavaScript对象。

  • subscribe(() => {}): 注册一个监听器,当state发生改变时,会调用该回调。

3. redux使用

3.1 安装redux
npm install redux
3.2 基本使用
  1. 新建redux目录,在redux目录下新建store.jsxxx.js
  2. store.js
// 引入legacy_createStore,专门用于创建redux中最核心的store对象
import { legacy_createStore } from 'redux'
// 引入为xxx组件服务的reducer
import countReducer from './count_reducer'

// 用于暴露一个store对象,整个应用只有一个store
export default legacy_createStore(countReducer)
  1. xxx.js(这里是count_reducer.js

创建一个为xxx组件服务的reducer,reducer的本质就是一个函数

countReducer有两个参数:preState(之前的状态)、action(动作对象)

reducer第一次调用时,是store自动触发的,传递的preState是undefined,action是@@REDUX/INITxxx

export default function countReducer(preState = 0, action) {
    // 从action对象中获取:type、data
    const { type, data } = action
    // 根据type决定如何加工数据
    switch (type) {
        case 'increment':
            return preState + data 
        case 'decrement':
            return preState - data
        default:
            return preState
    }
}
  1. getState:获取store中的state数据
store.getState()
  1. dispatchstore派发数据(更新数据)
store.dispatch({ type: 'increment', data: xxx })
  1. subscribe:监听store中的数据变化
store.subscribe(() => {
  	// 调用render,渲染页面
})

组件中使用:

componentDidMount() {
    store.subscribe(() => {
        this.setState({})
    })
}

全局index.js中使用:

import store from './redux/store'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
)

store.subscribe(() => {
  root.render(
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  )
})
3.3 异步action
  1. action分类:
  • 同步action:一般对象类型,{type: string, data: any}
  • 异步action:函数类型
  1. 异步action的使用:

组件中:

import React, { Component } from 'react'
import store from '../redux/store'
import { createIncrementAction, createAsyncIncrementAction } from '../redux/count_action'

export default class count extends Component {
    handleAsyncIncrement = () => {
        const { value } = this.selectNumber
        store.dispatch(createAsyncIncrementAction(value))
    }

    render() {
        return (
            <div>
                <h1>当前求和为: {store.getState()}</h1>
                <select ref={c => this.selectNumber = c}>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
                <button onClick={this.handleAsyncIncrement}>异步加</button>
            </div>
        )
    }
}

count_action.js中(用于创建action对象):

// 异步action:指action的值是函数,异步action中都会调用同步action去真正的操作数据
export const createAsyncIncrementAction = (data) => {
    return (dispatch) => {
      	// 由redux调用,传递参数dispatch
        setTimeout(() => {
            dispatch({ type: 'increment', data: data * 1 })
        }, 1000)
    }
}

⚠️注意:redux的action默认是不支持函数类型的,需要使用中间件redux-thunk

安装:

npm install redux-thunk

store.js中:

import { legacy_createStore, applyMiddleware } from 'redux'
// 引入redux-thunk,用于支持异步action
import { thunk } from 'redux-thunk'
import countReducer from './count_reducer'

export default legacy_createStore(countReducer, applyMiddleware(thunk))

这样就可以使用异步action了。

二、react-redux

在这里插入图片描述

react-redux搭配redux使用步骤:

1. 目录结构

在这里插入图片描述

  • components: 存放UI组件
  • containers: 存放容器组件
  • redux: 存放redux仓库

2. react-redux的基本使用

2.1 redux

store.js: 创建store

import { legacy_createStore, applyMiddleware } from 'redux'
import count_reducer from './count_reducer'
import { thunk } from 'redux-thunk'

export default legacy_createStore(count_reducer, applyMiddleware(thunk))

count_reducer.js

export default function countReducer(preState = 0, action) {
    const { type, data } = action
    switch (type) {
        case 'ADD':
            return preState + data
        default:
            return preState
    }
}
2.2 containers(容器组件): containers/count.jsx

容器组件的store是靠父组件的props传递进去的(2.3的App.jsx),而不是自己import引入的

// 引入cont的ui组件
import CountUI from '../components/count'
// 引入connect用于连接ui组件和redux
import { connect } from 'react-redux'

// 1. mapStateToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的key,value就作为传递给ui组件props的value
// 3. mapStateToProps函数的本质:传递状态,把状态传递给props
function mapStateToProps(state) {
    return {
        count: state
    }
}

// 1. mapDispatchToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的key,value就作为传递给ui组件props的value
// 3. mapDispatchToProps函数的本质:传递操作状态的方法
function mapDispatchToProps(dispatch) {
    return {
        add: () => {
            console.log('add')
            dispatch({ type: 'ADD', data: 1 })
        }
    }
}

// 使用connect()()创建并暴露一CountUI的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
2.3 App.jsx

⚠️注意:这里引入的Count组件是容器组件(2.2 创建的),而不是UI组件

通过props的方式,将store传入给容器组件,在mapStateToProps中就可以获取到statemapDispatchToProps中可获取到dispatch

import React, { Component } from 'react'
import Count from './containers/count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return <>
      <div>
        <h1>App</h1>
        <Count store={store} />
      </div>
    </>
  }
}
2.4 components(UI组件): components/count.jsx

这个时候通过this.props即可获取redux的store仓库中数据和方法(在2.2容器组件定义的数据和方法)

import React, { Component } from 'react'

export default class count extends Component {
  handleAdd = () => {
    this.props.add()
  }

  render() {
    // console.log('ui组件props', this.props)
    return (
      <div>
        <p>num为: {this.props.count}</p>
        <button onClick={this.handleAdd}>+1</button>
      </div>
    )
  }
}

3. 优化

3.1 优化1:容器组件mapDispatchToProps的简写

mapDispatchToProps可以是函数,也可以是对象

value只要返回action,react-redux自动dispatch分发action

// 引入cont的ui组件
import CountUI from '../components/count'
// 引入connect用于连接ui组件和redux
import { connect } from 'react-redux'

export default connect(
    state => ({
        count: state
    }),
    // mapDispatchToProps的简写
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(CountUI)
3.2 优化2:不用再手动监测redux数据的变化,react-redux自动监测
3.3 优化3:Provider使用

如果有多个容器组件,需要每个容器组件都传入store,就类似这样:

<Count store={store} />
<Demo store={store} />
<Text store={store} />

优化:可以通过Provider向每个容器组件都传入store,在入口文件中如下使用:

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

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
)

需要组件的地方直接使用即可:

<Count />
3.4 整合:UI组件+容器组件
import React, { Component } from 'react'
import { connect } from 'react-redux'

// ui组件
class count extends Component {
    handleAdd = () => {
        this.props.add()
    }

    render() {
        return (
            <div>
                <p>num为: {this.props.count}</p>
                <button onClick={this.handleAdd}>+1</button>
            </div>
        )
    }
}

// 容器组件
export default connect(
    state => ({
        count: state
    }),
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(count)

4. 多个reducer的使用

store.js中:

import { legacy_createStore, applyMiddleware, combineReducers } from 'redux'
import count_reducer from './reducers/count'
import person_redercer from './reducers/person'
import { thunk } from 'redux-thunk'

// 汇总所有的reducer变成一个总的reducer
const allReducer = combineReducers({
  count: count_reducer,
  personList: person_redercer
})

export default legacy_createStore(allReducer, applyMiddleware(thunk))

容器组件中:

import React, { Component } from 'react'
import { connect } from 'react-redux'

class count extends Component {
    handleAdd = () => {
      	// 操作状态的方法不需要区分
        this.props.add()
    }

    render() {
        return (
            <div>
                <p>num为: {this.props.count}</p>
                <button onClick={this.handleAdd}>+1</button>
                <div>{this.props.personList.length}</div>
            </div>
        )
    }
}

export default connect(
    state => ({
      	// state.xxx:xxx是根据store.js中的allReducer中的key值决定的
        count: state.count,
        personList: state.personList
    }),
    {
        add: () => ({ type: 'ADD', data: 1 })
    }
)(count)

5. redux开发者工具的使用

5.1 下载浏览器插件

下载地址:https://chrome.zzzmh.cn/info/lmhkpmbekcpmknklioeibfkpmmfibljd

5.2 项目中安装
npm i redux-devtools-extension
5.3 在store.js中配置
import { legacy_createStore, applyMiddleware } from 'redux'
import count_reducer from './reducers/count'
import { thunk } from 'redux-thunk'
// 引入开发者工具
import { composeWithDevTools } from 'redux-devtools-extension'

export default legacy_createStore(count_reducer, composeWithDevTools(applyMiddleware(thunk)))

这样浏览器插件就会亮了。

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值