安装依赖
yarn add redux redux-thunk
在src文件夹下创建redux目录,并创建store.js和test_reducer.js
在store.js中引入redux的createStore方法和test_reducer.js
// 该文件用于暴露一个store对象,整个项目应只有一个store对象
import { createStore, applyMiddleware } from "redux"
import testData from './test_reducer'
在store配置store,并导出store
...
const store = createStore(testData)
export default store
配置reducer,reducer接受的action对象{type:'自定义类型',data:'数据'}
/*
* 专门为test组件创建的reducer对象
* 接收两个参数,一个是之前的状态,一个是动作参数
*/
const initState = 0 // 初始化
function testReducer(preState = initState, action) {
const { type, data } = action
switch (type) {
case ADD:
return preState + data
case DECREMENT:
return preState - data
default:
// 项目创建时,store会自动调用一次,初始化
return preState;
}
}
export default testReducer
这样最精简的redux就配置好了,到组件中使用store
注:store状态更新后不会自动调用render方法,需要手动添加;函数式组件无生命周期
import React, { useRef, useState,useEffect } from 'react'
import store from '../../../redux/store'
export default function Right() {
const selected = useRef(null)
const [update,setUpdate] = useState({})
// useEffect模拟componentDidMount()生命周期---函数式组件没有生命周期
useEffect(() => {
// store.subscribe()是redux提供的,监测store更新的函数
store.subscribe(() => {
// 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新
setUpdate({})
})
})
// 数据添加下拉框得值
const addNumber = () => {
store.dispatch({ type: 'add', data: Number(selected.current.value) })
setUpdate({})
}
// 数据减下拉框得值
const decrement = () => {
store.dispatch({ type: 'decrement', data: Number(selected.current.value) })
}
// 如果数据是奇数,增加下拉框得值
const incrementIfOdd = () => {
if(store.getState() % 2 !== 0)store.dispatch({ type: 'add', data: Number(selected.current.value) })
}
// 异步增加
const incrementIfAsync = () => {
setTimeout(() => {
store.dispatch({ type: 'add', data: Number(selected.current.value) })
}, 1000)
}
return (
<div className='page1_right_content'>
<p>当前求和为:{store.getState()}</p>
<select name="" id="" ref={selected}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button onClick={addNumber}>+</button>
<button onClick={decrement}>-</button>
<button onClick={incrementIfOdd}>是否为奇数再加</button>
<button onClick={incrementIfAsync}>异步加</button>
</div>
)
}
这样最精简得redux用法就完成了...,下面我们来优化redux的使用
------------------------------------------------------------------------------------------------------------------------------
在redux目录下添加test_action.js文件,并配置对应方法
// 为test组件创建的action对象
// 加数据的方法
export const createAddAction = (data)=> ({type:'add',data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:'decrement',data})
这样我们调用reducer的时候就只需要使用action中的方法就行了
...
import { createAddAction, createDecrementAction} from '../../../redux/test_action'
...
const addNumber = () => {
store.dispatch(createAddAction(selected.current.value*1))
}
但是我们之前配置的action都是同步的,我们通常会遇到异步修改数据,所以还需要配置一个异步的action
// 为test组件创建的action对象
// 同步action,返回object
// 加数据的方法
export const createAddAction = (data)=> ({type:ADD,data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:DECREMENT,data})
// 异步action返回的值为函数,异步action中一般都会调用同步action
export const createAsyncAction = (data, time) => {
// recucer调用action的时候会返回一个dispatch参数,就不需要再引入一次store了
return dispatch => {
setTimeout(() => {
dispatch(createAddAction(data))
}, time)
}
}
使用异步action
...
const incrementIfAsync = () => {
store.dispatch(createAsyncAction(selected.current.value*1, 1000))
}
此时,你调用异步操作会发现程序报错了,这里我们还需要借助一个依赖redux-thunk来支持我们的异步操作
首先在store中配置异步的中间件,需要引入redux中的applyMiddleware方法
// 该文件用于暴露一个store对象,整个项目应只有一个store对象
import { createStore, applyMiddleware } from "redux"
// 引入 redux-thunk,用于支持异步操作的执行
import thunk from 'redux-thunk'
import testData from './test_reducer'
const store = createStore(testData,applyMiddleware(thunk))
export default store
这时你的程序应该能跑通了吧,不过还没完,我们还需要做进一步优化
在redux目录下再创建一个文件constant.js,用于定义常量
// 定义常量,便于管理同时防止程序员单词写错
export const ADD = 'add'
export const DECREMENT = 'decrement'
然后将使用到的字符串全部替换为常量
test_reducer.js修改特定字符串为常量(完整代码)
/*
* 专门为test组件(page1_right)创建的reducer对象
* 接收两个参数,一个是之前的状态,一个是动作参数
*/
import { ADD, DECREMENT } from "./constant"
const initState = 0
function testReducer(preState = initState, action) {
// 处于初始化状态
// if(preState === 'undefined') preState = 0
const { type, data } = action
switch (type) {
case ADD:
return preState + data
case DECREMENT:
return preState - data
default:
// 项目创建时,store会自动调用一次,初始化
return preState;
}
}
export default testReducer
test_action.js修改特定字符串为常量(完整代码)
import { ADD, DECREMENT } from "./constant"
// 为test组件创建的action对象
// 同步action,返回object
// 加数据的方法
export const createAddAction = (data)=> ({type:ADD,data})
// 减数据的方法
export const createDecrementAction = (data) => ({type:DECREMENT,data})
// 异步action返回的值为函数,异步action中一般都会调用同步action
export const createAsyncAction = (data, time) => {
// recucer调用action的时候会返回一个dispatch参数,就不需要再引入一次store了
return dispatch => {
setTimeout(() => {
dispatch(createAddAction(data))
}, time)
}
}
调用的完整代码
import React, { useRef, useState,useEffect } from 'react'
import store from '../../../redux/store'
import { createAddAction, createDecrementAction,createAsyncAction } from '../../../redux/test_action'
export default function Right() {
const selected = useRef(null)
const [update,setUpdate] = useState({})
// useEffect模拟componentDidMount()生命周期---函数式组件没有生命周期
useEffect(() => {
// store.subscribe()是redux提供的,监测store更新的函数
store.subscribe(() => {
// 当store数据更新后执行 setUpdate() ,组件重新加载,实现界面store数据更新
setUpdate({})
})
})
const addNumber = () => {
store.dispatch(createAddAction(selected.current.value*1))
}
const decrement = () => {
store.dispatch(createDecrementAction(selected.current.value*1))
}
const incrementIfOdd = () => {
if(store.getState() % 2 !== 0)store.dispatch(createAddAction(selected.current.value*1))
}
const incrementIfAsync = () => {
store.dispatch(createAsyncAction(selected.current.value*1, 1000))
}
return (
<div className='page1_right_content'>
<p>当前求和为:{store.getState()}</p>
<select name="" id="" ref={selected}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button onClick={addNumber}>+</button>
<button onClick={decrement}>-</button>
<button onClick={incrementIfOdd}>是否为奇数再加</button>
<button onClick={incrementIfAsync}>异步加</button>
</div>
)
}
---------------------------------------------------------------------------------------------------------------------------------
使用react-redux(配合redux)
npm i react-redux
react-redux需要创建一个容器组件包裹UI组件,然后在容器组件中调用状态方法
在src目录下创建一个containers目录,并创建test目录及TestContainer.jsx
// 引入之前创建的Test组件---ui组件
import TestUI from '../../pages/Test'
// 引入react-redux
import { connect } from 'react-redux'
// 使用connect()(TestUI)创建并暴露一个TestUI的容器组件
export default connect()(TestUI)
同时App.jsx(或其他组件)引用Test组件就要改为引用容器组件,同时引入reudx中的store,并将其传递到容器组件
import TestContainer from './containers/test/TestContainer';
import store from './redux/store'
import './App.css';
function App() {
return (
<div className="App">
<TestContainer store={store} name="test"></TestContainer>
</div>
);
}
export default App;
当容器组件将store接受到后,就可以进行状态的获取和修改了
react-redux的connect方法可以传入两个参数[返回状态的方法,返回操作状态的方法]
第一个参数会接收state,第二个则接收dispatch方法
// 引入ui组件
import TestUI from '../../pages/Test'
// 引入react-redux
import { connect } from 'react-redux'
import { createAddAction, createDecrementAction,createAsyncAction } from '../../redux/test_action'
// 不能直接返回数值,都返回一个对象,
function mapStateToProps(state) { // 映射状态
return {count: state}
}
function mapDispatchToProps(dispatch) { // 映射操作状态的方法
return {
add: (data) => dispatch(createAddAction(data)),
decrement: (data) => dispatch(createDecrementAction(data)),
asyncAdd: (data,time) => dispatch(createAsyncAction(data, time))
}
}
// 使用connect()(TestUI)创建并暴露一个TestUI的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(TestUI)
connect方法的两个参数必要以对象(函数)的方式传递,名字可自定义
然后再UI组件中可以通过props接收到传递的状态或方法
接下来就在UI组件中使用props获取和修改状态
import React, { useRef } from 'react'
export default function Test(props) {
console.log('props', props);
const selected = useRef(null)
const addNumber = () => {
props.add(selected.current.value*1)
}
const decrement = () => {
props.decrement(selected.current.value*1)
}
const incrementIfOdd = () => {
if (props.count % 2 !== 0) props.add(selected.current.value*1)
}
const incrementIfAsync = () => {
props.asyncAdd(selected.current.value*1,1000)
}
return (
<div className='page1_right_content'>
<p>当前求和为:{props.count}</p>
<select name="" id="" ref={selected}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<button onClick={addNumber}>+</button>
<button onClick={decrement}>-</button>
<button onClick={incrementIfOdd}>是否为奇数再加</button>
<button onClick={incrementIfAsync}>异步加</button>
</div>
)
}