Redux

redux

工作流

在这里插入图片描述

dispatch派发action来触发reducer更改state

在redux/store.js下

/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//暴露store
export default createStore(countReducer,applyMiddleware(thunk))

redux/constant.js

/* 
	该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

redux/count_reducer.js文件中

/* 
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT} from './constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log(preState);
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return preState + data
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

redux/count_action.js文件中

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

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => ({type:INCREMENT,data})
export const createDecrementAction = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const createIncrementAsyncAction = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(createIncrementAction(data))
		},time)
	}
}

使用

在components/Count/index.js

import React, { Component } from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import {
	createIncrementAction,
	createDecrementAction,
	createIncrementAsyncAction
} from '../../redux/count_action'

export default class Count extends Component {

	state = {carName:'奔驰c63'}

	/* componentDidMount(){
		//检测redux中状态的变化,只要变化,就调用render
		store.subscribe(()=>{
			this.setState({})
		})
	} */

	//加法
	increment = ()=>{
		const {value} = this.selectNumber
		store.dispatch(createIncrementAction(value*1))
	}
	//减法
	decrement = ()=>{
		const {value} = this.selectNumber
		store.dispatch(createDecrementAction(value*1))
	}
	//奇数再加
	incrementIfOdd = ()=>{
		const {value} = this.selectNumber
		const count = store.getState()
		if(count % 2 !== 0){
			store.dispatch(createIncrementAction(value*1))
		}
	}
	//异步加
	incrementAsync = ()=>{
		const {value} = this.selectNumber
		// setTimeout(()=>{
			store.dispatch(createIncrementAsyncAction(value*1,500))
		// },500)
	}

	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>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

在App.jsx中

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'

ReactDOM.render(<App/>,document.getElementById('root'))

store.subscribe(()=>{
	ReactDOM.render(<App/>,document.getElementById('root'))
})

react-redux

用于连接redux和UI组件的一个容器,这个容器由react-redux提供

容器组件相当于一个中介,和redux打交道,通过props和UI组件打交道

在container/Count/index.js里创建容器组件,使用connect连接容器组件和UI组件

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
	createIncrementAction,
	createDecrementAction,
	createIncrementAsyncAction
} from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

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

/* 
	1.mapDispatchToProps函数返回的是一个对象;
	2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
	3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch){
	return {
		jia:number => dispatch(createIncrementAction(number)),
		jian:number => dispatch(createDecrementAction(number)),
		jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
	}
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

在components/Count/index.js,可以在以下代码中看出,里面基本没有和redux打交道的代码了。

import React, { Component } from 'react'

export default class Count extends Component {

	state = {carName:'奔驰c63'}

	//加法
	increment = ()=>{
		const {value} = this.selectNumber
		this.props.jia(value*1)
	}
	//减法
	decrement = ()=>{
		const {value} = this.selectNumber
		this.props.jian(value*1)
	}
	//奇数再加
	incrementIfOdd = ()=>{
		const {value} = this.selectNumber
		if(this.props.count % 2 !== 0){
			this.props.jia(value*1)
		}
	}
	//异步加
	incrementAsync = ()=>{
		const {value} = this.selectNumber
		this.props.jiaAsync(value*1,500)
	}

	render() {
		//console.log('UI组件接收到的props是',this.props);
		return (
			<div>
				<h1>当前求和为:{this.props.count}</h1>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

在App.jsx中

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

export default class App extends Component {
	render() {
		return (
			<div>
				{/* 给容器组件传递store */}
				<Count store={store} />
			</div>
		)
	}
}

这里开始优化上面的代码,优化内容如下

1. 容器组件和UI组件整合一个文件
2. 无需自己给容器组件传递store,给App包裹一个< Provider store={store} >即可。这样App里的每个容器组件都会通信redux
3. 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。(就是不在使用store.subscribe来订阅store)
4. mapDispatchToProps也可以简单的写成一个对象

这里只写container/Count/index.jsx

import React, { Component } from 'react'
//引入action
import {
	createIncrementAction,
	createDecrementAction,
	createIncrementAsyncAction
} from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

//定义UI组件
class Count extends Component {

	state = {carName:'奔驰c63'}

	//加法
	increment = ()=>{
		const {value} = this.selectNumber
		this.props.jia(value*1)
	}
	//减法
	decrement = ()=>{
		const {value} = this.selectNumber
		this.props.jian(value*1)
	}
	//奇数再加
	incrementIfOdd = ()=>{
		const {value} = this.selectNumber
		if(this.props.count % 2 !== 0){
			this.props.jia(value*1)
		}
	}
	//异步加
	incrementAsync = ()=>{
		const {value} = this.selectNumber
		this.props.jiaAsync(value*1,500)
	}

	render() {
		//console.log('UI组件接收到的props是',this.props);
		return (
			<div>
				<h1>当前求和为:{this.props.count}</h1>
				<select ref={c => this.selectNumber = c}>
					<option value="1">1</option>
					<option value="2">2</option>
					<option value="3">3</option>
				</select>&nbsp;
				<button onClick={this.increment}>+</button>&nbsp;
				<button onClick={this.decrement}>-</button>&nbsp;
				<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
				<button onClick={this.incrementAsync}>异步加</button>&nbsp;
			</div>
		)
	}
}

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
	state => ({count:state}),

	//mapDispatchToProps的一般写法
	/* dispatch => ({
		jia:number => dispatch(createIncrementAction(number)),
		jian:number => dispatch(createDecrementAction(number)),
		jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
	}) */

	//mapDispatchToProps的简写
	{
		jia:createIncrementAction,
		jian:createDecrementAction,
		jiaAsync:createIncrementAsyncAction,
	}
)(Count)

上面的代码只有一个Count组件,没有完成数据共享,下面我们写个多个组件的数据共享版的

  1. 定义一个Pserson组件,和Count组件通过redux共享数据
  2. 为Person组件编写:reducer、action,配置constant常量
  3. 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,合并后的总状态是一个对象!!!
  4. 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”

目录结构如下:
在这里插入图片描述
在其目录结构下的redux/store.js

/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Count组件服务的reducer
import personReducer from './reducers/person'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

//汇总所有的reducer变为一个总的reducer
const allReducer = combineReducers({
	he:countReducer,
	rens:personReducer
})

//暴露store
export default createStore(allReducer,applyMiddleware(thunk))

react-redux开发者工具的使用

  1. npm  install redux-devtools-extension
  2. store中进行配置
import {composeWithDevTools} from 'redux-devtools-extension'
const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

react-redux最终版

​ (1).所有变量名字要规范,尽量触发对象的简写形式。

​ (2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

在这里插入图片描述

其reducers/index.js内容如下

redux/store.js的文件内容如下
在这里插入图片描述

redux-toolkit

npm i @reduxjs/toolkit react-redux
npm i redux-devtools -D

使用

在store/fetures/counterSlice.js

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
    name:'counter',
    initialState:{
        count:1,
        title:'redux toolkit pre'
    },
    reducer:{
        increment(state,{ payload }){
            state.count = state.count + payload.step // 内置了immutable
        },
        decrement(state){
            state.count -= 1
        }
    }
})

export const { increment,decrement } = counterlice.actions

export const asyncIncrement = (payload) => (dispatch) => {
    setTimeout(() => {
        dispatch(increment(payload))
    },2000)
}
export default counterSlice.reducer

在store/index.js

import { configureStore } from '@reduxjs/toolkit'
import counterSlice from './fetures/counterSlice'

export default configureStore({
    reducers:{
        'counter':counterSlice
    }
})

在index.js中

import store from './store'
import { Provider } from 'react-redux'

ReactDOM.hydrate(
    <Provider store={store}>
      <App />
    </Provider>,
    document.getElementById('root')
  )

这里,我们就创建好了store,我们可以通过redux-devtools在浏览器中查看store

在这里比如在App组件中来使用我们的共享数据store

import { useSelector,useDispatch } from 'react-redux'
import { increment,asyncIncrement } from './store/fetures/counterSlice'

function App() {
    const { count } = useSelector(state => state.counter) // state.counter 对应的是store/index.js目录的counter,这里又映射到在store/fetures/counterSlice.js文件里的counterReducer
    const dispatch = useDispatch()
    return(
     <div className='App'>
        <Button
           onClick={() => {
           		dispatch(increment({ step:1 }))         
           }}    
        >
            同步加: {count}
        </Button>
        <hr />
        <Button
           onClick={() => {
           		dispatch(asyncIncrement({ step:2 }))         
           }}    
        >
            异步加: {count}
        </Button>  
     </div>
    )
}

我们实现了最基础的案例

但在一个项目中可能有多个reducer,这里我们在上一个基础案例的基础上,增加一个movie的reducer。

同时,还来学习一个createAsyncThunk的API,来创建异步的action

createAsyncThunk创建一个异步的Action,这个方法被触发的时候会有三个状态

pending 进行中

fulfilled 成功

reject 失败

在store/fetures/movieSlice.js

import { createSlice,createAsyncThunk } from '@reduxjs/toolkit'
import { increment } from './counterSlice'

// 此处就是一个请求数据的函数
const loadMoviesAPI = () => 
	fetch('http://www.searchList')
		.then(res => res.json());

export const loadData = createAsyncThunk('movie/loadData',async (payload,store) => {
    const res = await loadMoviesAPI()
    store.dispatch(loadDataEnd([1,2,3]))
    return res // 此处的返回结果会在.fulfilled中作为payload的值
})

export const movieSlice = createSlice({
    name:'movie',
    initialState:{
        list:[],
        totals:0
    },
    reducer:{
        loadDataEnd(state,{ payload }){
            state.list = payload;
            state.totals = payload.length;
        }
    },
    // extraReducer里的reducer都是订阅模式,由发布者触发
    extraReducer:{
        // 可以触发其他slice中的数据关联改变
        [increment](state,payload){
            state.list.push(1);
            state.totals -= 1;
        },
        [loadData.fulfilled](state,{ payload }){
            console.log(payload)
            state.list = payload.data.list
        },
        [loadData.rejected](state,err){
            console.log(err)
        },
        [loadData.pending](state,_){
            console.log('进行中')
        }
    }
})

export const { loadDataEnd } = movieSlice.actions
export default counterSlice.reducer

在store/index.js

import { configureStore } from '@reduxjs/toolkit'
import counterSlice from './fetures/counterSlice'
import movieSlice from './fetures/movieSlice'

export default configureStore({
    reducers:{
        'counter':counterSlice,
        'movie':movieSlice
    }
})

在App.js中使用movieReducer

import { useEffect } from 'react'
import { loadData } from './store/features/movieSlice'

function App() {
    const dispatch = useDispatch()
    // 模拟class组件的componentDidMount
    useEffect(() => {
        dispatch(loadData())
    },[])
    return(
     <div className='App'>
            Hello world
     </div>
    )
}

在App.js中使用movieReducer

import { useEffect } from 'react'
import { loadData } from './store/features/movieSlice'

function App() {
    const dispatch = useDispatch()
    // 模拟class组件的componentDidMount
    useEffect(() => {
        dispatch(loadData())
    },[])
    return(
     <div className='App'>
            Hello world
     </div>
    )
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值