1.redux工作原理
- 1.React components是我们自己定义的组件
- 2.React components会把需要做的事告诉Action Creators需要做什么
- 3.Action Creators会创建一个动作对象action
- 4.action中包含两个属性:type:动作类型,data:操作的数据
- 5.Action Creators将action派发(dispatch)进入Store中,dispatch可以将动作action对象传递下去,所以必须要有
- 6.进入Store,Store是核心仓库,负责全局掌控,本身不处理事件,就是一个指挥者
- 7.Store将需要处理的action对象给Reducers,同时将之前状态的动作对象也传递给Reducers
- 8.Reducer结合之前的action与当前的action进行事件处理,并将新的状态结果返回给Store,上图的return newState
- 9.Store将新的状态结果通过getState()传递给React components,改变组件当前状态 Reducers可以加工状态也可以初始化状态(只做一次,就是第一次),当Reducer初始化状态时,有action对象,previousState为undefined,传递的action中的type就是初始化,data是初始化的数据
2.redux初步实践
2.1 安装redux
npm i redux 或者yarn add redux
2.2 Store
//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
import {createStore} from 'redux'
//引入为count组件服务的reducer
import countReducer from './count_reducers'
//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
export default createStore(countReducer)
2.3 reducer
//创建一个为组件服务的reducer,它的本质是一个纯函数
//定义个初始值
let initState=0
//传入两个参数,一个为之前的状态preState,另一个是动作对象action
//设置默认值,初始化数据data
//这里的reducer是一个纯函数
export default function countReducers(preState =initState, action) {
//从action中拿到type与data
const {
type,
data
} = action
//根据type加工数据
switch (type) {
case 'increment':
return preState + data
case 'decrement':
return preState - data
default:
return preState
}
}
2.4 action
//分别封装两个action函数用来创建action对象
export const createIncrement=data=>({type:"increment",data})
export const createDecrement=data=>({type:"decrement",data})
2.5 constant
//定义常量文件
export const INCREMENT='increment'
export const DECREMENT='decrement'
2.6具体使用
2.6.1 引入store
import store from '../../redux/count_redux/store'
2.6.2 通过getstate可以获取初始状态
import store from '../../redux/count_redux/store'
2.6.3 引入action
import {createIncrement,createDecrement} from '../../redux/count_redux/count_action'
2.6.4 使用dispatch去传递action
//通知redux去加value
store.dispatch(createIncrement(value*1))
2.6.5 渲染render
//因为没有setstate,页面没有发生更新,需要监听redux,动态的去触发render函数
//使用setstate中去触发render
componentDidMount(){
store.subscribe((()=>{
this.setState({})
}))
}
//这样修改每次都需要调用store.subscribe(),因此有如下方式
//重新渲染根组件,触发render
store.subscribe(()=>{
ReactDOM.render(
<App />,
document.getElementById('root')
);
})
2.6.6 案例
import React, { Component } from 'react'
// 引入store用于获取store状态
import store from '../../redux/count_redux/store'
import {createIncrement,createDecrement} from '../../redux/count_redux/count_action'
export default class index extends Component {
componentDidMount(){
store.subscribe((()=>{
this.setState({})
}))
}
//加
increment=()=>{
let {value}=this.selectNumber
//通知redux去加value
store.dispatch(createIncrement(value*1))
}
//减
decrement=()=>{
let {value}=this.selectNumber
//通知redux去加value
store.dispatch(createDecrement(value*1))
}
//异步加
asyncIcrement=()=>{
let {value}=this.selectNumber
setTimeout(() => {
store.dispatch(createIncrement(value*1))
}, 500);
}
//奇数加
oddIcrement=()=>{
let {value}=this.selectNumber
let count=store.getState()
if(count%2!==0){
store.dispatch(createIncrement(value*1))
}
}
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.increment}>加</button>
<button onClick={this.decrement}>减</button>
<button onClick={this.asyncIcrement}>异步加</button>
<button onClick={this.oddIcrement}>奇数加</button>
</div>
)
}
}
3. 异步action
当我们需要在做一些定时器任务,发布订阅,网络请求就需要使用异步action,异步action是指它的值为一个函数,同步action是指它的值是一个对象
3.1 引入redux-thunk
yarn add redux-thunk
引入中间件,并且将中间件作为createStore的第二个参数传入,这里的第二个参数中间件是一个函数,可以传入一个参数为引入的thunk
//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
//引入applyMiddleware中间件
import {createStore,applyMiddleware} from 'redux'
//引入为count组件服务的reducer
import countReducer from './count_reducers'
//引入react-thunk,用于支持异步action
import thunk from 'redux-thunk'
//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
export default createStore(countReducer,applyMiddleware(thunk))
3.2 异步处理过程
//在action中进行异步操作,返回一个函数,作为异步处理方案
//分别封装两个action函数用来创建action对象
import {
INCREMENT,
DECREMENT
} from './constant'
import store from './store'
export const createIncrement = data => ({
type: INCREMENT,
data
})
export const createDecrement = data => ({
type: DECREMENT,
data
})
//异步的action就是指action返回的是一个函数,并且该函数的返回值是一个同步的action
export const createAsyncIncrement = (data, time) => {
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrement(data))
}, time);
}
}
3.3 在组件中触发
asyncIcrement=()=>{
let {value}=this.selectNumber
store.dispatch(createAsyncIncrement(value*1,500))
}
4.React-redux
4.1 模型图
4.2 容器组件
1.在src目录下定义一个containers文件夹存放容器组件
2.在containers文件夹下新建一个math容器组件
3.容器组件需要借助react-redux
4.安装react-redux
yarn add react-redux
5.引入UI组件
// 引入math(ui组件)
import MathUI from '../../components/Math'
6.引入redux
//需要在ui组件使用的时候在标签上引入
//引入
import Math from './containers/math'
//使用
<Math store={store}/>
7.引入connect用于连接ui组件和redux
//引入connect用于连接ui组件和redux
import {connect} from 'react-redux'
8.使用connect创建并暴露组件
//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件
export default connect()(MathUI)
4.3 容器组件的使用案例
//container.jsx
// 引入math(ui组件)
import MathUI from '../../components/Math'
import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/count_redux/count_action'
//引入store
// import store from '../../redux/count_redux/store'
//引入connect用于连接ui组件和redux
import {connect} from 'react-redux'
// mapStateProps返回一个对象
// mapStateProps的返回值作为状态传递给UI组件
//返回对象有key,value,相当于props中的key和value
function mapStateProps(state){
return{count:state}
}
//mapDispatchToProps返回一个对象
//mapDispatchToProps的返回值作为操作状态的方法传递给ui组件,
//key就是传递给ui组件中props中的key,value就是传递给ui组件的props中的value
function mapDispatchToProps(dispatch){
return {
jia:(data)=>{
dispatch(createIncrement(data))
},
jian:(data)=>{
dispatch(createDecrement(data))
},
jiaAsync:(data,time)=>{
dispatch(createAsyncIncrement(data,time))
}
}
}
//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件
//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(mapStateProps,mapDispatchToProps)(MathUI)
- 1.这里的mapStateProp和mapDispatchToProps可以映射容器组件和ui组件。
- 2.这里的容器组件就相当于ui组件的父组件,容器组件通过mapStateProp来传递状态到ui组件。
- 3.这里的mapStateProp函数可以接受到一个参数,这个参数是state获取redux的初始状态,等价于redux中的store.getState(),并且返回一个对象,key就是传递给ui组件的props的key,value就是传递给ui组件的props的value。
- 4.这里的mapDispatchToProps作为操作状态的方法,在ui组件中可以使用this.props。xxx()调用,mapDispatchToProps的参数是dispatch,用于将action传递给redux并且它返回一个对象,对象的key作为方法名,value是一个函数用于处理初始状态。
//在组件中
import React, { Component } from 'react'
export default class index extends Component {
componentDidMount(){
}
//加
increment=()=>{
let {value}=this.selectNumber
this.props.jia(value*1)
}
//减
decrement=()=>{
let {value}=this.selectNumber
this.props.jian(value*1)
}
//异步加
asyncIcrement=()=>{
let {value}=this.selectNumber
this.props.jiaAsync(value*1,500)
}
//奇数加
oddIcrement=()=>{
let {value}=this.selectNumber
if(this.props.count%2!=0){
this.props.jia(value*1)
}
}
render() {
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>
<button onClick={this.increment}>加</button>
<button onClick={this.decrement}>减</button>
<button onClick={this.asyncIcrement}>异步加</button>
<button onClick={this.oddIcrement}>奇数加</button>
</div>
)
}
}
4.4 容器组件的简化写法
// 引入math(ui组件)
import MathUI from '../../components/Math'
import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/count_redux/count_action'
//引入store
// import store from '../../redux/count_redux/store'
//引入connect用于连接ui组件和redux
import {connect} from 'react-redux'
//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件
//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(
state=>({
count:state
})
//在react-redux中可以将action自动dispatch给redux,
//如果两个参数都是函数,第二个参数可以写成一个对象
,{
jia:createIncrement,
jian:createDecrement,
jiaAsync:createAsyncIncrement
})(MathUI)
5. provider的使用
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import store from './redux/count_redux/store'
ReactDOM.render(
// React.StrictMode检查app和app包裹的组件是否合理
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
ddocument.getElementById('root') );
在index.js中使用provide,provide上直接使用store,容器组件得到store,避免多个ui组件上调用store,不然就需要逐一的在ui组件标签上引入store,例如如下:
import Login from './components/Login'
import Register from "./components/Register"
import store from './redux/store'
function App() {
return (
<div>
<Login store={store}/>
<hr/>
<Register store={store}/>
</div>
);
}
export default App;
6.redux开发工具的使用
2.在项目安装
yarn add redux-devtools-extension
3.在store文件夹中引入
import {composeWithDevTools} from 'redux-devtools-extension'
//传递到创建store的第二个参数,并将中间件applyMiddleware作为参数
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
4.成功安装如图
7.React-redux开发案例
7.1 store
1.需要将多个reducer和并放在一个对象中
2.引入combineReducers
3.通过调用combineReducers,入参一个对象,这里的key可以自己命名,value分别是引入的reducer
//该文件用于暴露store对象
//引入createStore,专门用于创建redux中最核心的store对象
//引入applyMiddleware中间件
import {createStore,applyMiddleware,combineReducers} from 'redux'
//引入为count组件服务的reducer
import countReducer from './reducers/count'
import personReducer from './reducers/person'
//引入react-thunk,用于支持异步action
import thunk from 'redux-thunk'
//汇总reducer变成一个总的reducer
const allReducer=combineReducers({
he:countReducer,
rens:personReducer
})
//store中需要一个为之服务的reducer,因此将引入的reducer当参数传入这个创建的store中
//并且将这个store对象暴露出去
//
export default createStore(allReducer,applyMiddleware(thunk
7.2 Action
创建一个对象,有两个参数,第一个参数是type,第二个参数是需要传递过来的数据data
7.2.1 count_action
import { INCREMENT, DECREMENT} from '../constant'
export const createIncrement=data=>
({
type:INCREMENT,
data
})
export const createDecrement=data=>(
{
type:DECREMENT,
data
}
)
//异步action
export const createAsyncIncrement=(data,time)=>{
return dispatch=>{
setTimeout(() => {
dispatch(incrementAction(data))
}, time);
}
}
7.2.2 person_action
import{ADD_PERSON}from '../constant'
export const createPerson=personObj=>({
type:ADD_PERSON,
data:personObj
})
7.3 Constant
存放常量文件
//暴露加法和减法
export const INCREMENT='increment'
export const DECREMENT='decrement'
export const ADD_PERSON='add_crement'
7.4 Reducers
7.4.1 count_reducer
- 做一些逻辑处理,这里的函数接受两个参数,第一个参数是之前的值,第二个参数是外界传递过来的action,
- action是一个对象,对象的参数是type与data
import { INCREMENT, DECREMENT} from '../constant'
const initState=0
export default function countReducer(preState = initState, action) {
const {
type,
data
} = action
switch (type) {
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
7.4.2 person_reducer
import{ADD_PERSON}from '../constant'
let person=[{id:"001",name:"ws",age:23}]
export default function PersonSum(preState=person,action){
let {type,data}=action
switch (type) {
case ADD_PERSON:
return [data,...preState]
default:
return preState
}
7.5 组件内的使用
这里的组件有两个,一个是ui组件一个是容器组件,ui组件主要进行展示,容器组件做一些数据加工,容器组件包裹ui组件,并将容器组件暴露出去
7.5.1 Login Comoponents
import{ createIncrement,createDecrement, createAsyncIncrement} from '../../redux/actions/count'
//引入store
// import store from '../../redux/count_redux/store'
//引入connect用于连接ui组件和redux
import {connect} from 'react-redux'
import React, { Component } from 'react'
class index extends Component {
//加
increment=()=>{
let {value}=this.selectNumber
this.props.jia(value*1)
}
decrement=()=>{
let {value}=this.selectNumber
this.props.jian(value*1)
}
//异步加
asyncIcrement=()=>{
let {value}=this.selectNumber
this.props.jiaAsync(value*1,500)
}
//奇数加
oddIcrement=()=>{
let {value}=this.selectNumber
if(this.props.count%2!=0){
this.props.jia(value*1)
}
}
render() {
return (
<div>
<h1>person组件下方总人数{this.props.renshu}</h1>
<h2>当前显示的数是:{this.props.count}</h2>
<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.increment}>加</button>
<button onClick={this.decrement}>减</button>
<button onClick={this.asyncIcrement}>异步加</button>
<button onClick={this.oddIcrement}>奇数加</button>
</div>
)
}
}
//高阶函数,连接ui组件
//使用connect创建并暴露一个math组件
//connect第一个函数是映射ui组件和容器组件的关系,第二个组件是连接ui组件和容器组件
export default connect(
state=>({
count:state.he,
renshu:state.rens.length
})
//在react-redux中可以将action自动dispatch给redux,
//如果两个参数都是函数,第二个参数可以写成一个对象
,{
jia:createIncrement,
jian:createDecrement,
jiaAsync:createAsyncIncrement
})(index)
容器组件主要是通过connect进行使用
- 1.首先需要下载react-redux,yarn add react-redux
- 2.引入connect
- 3.connect中可以调用两次,第一次调用可以传递两个函数,第一个函数返回一个对象,对象的值可以通过state.去获取reducer的值,第二个函数返回一个对象,key是我们自己定义的,value是函数。
- 4.这里的函数可以是处理数据的action,action需要引入进来
- 5.connect的第二次调用需要将ui组件传递进去
7.5.2 Register Components
下载nanoid ,生成一个随机且不重复的id
yarn add nanoid 或者 npm i nanoid
import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import {connect} from 'react-redux'
import {createPerson}from "../../redux/actions/person"
class index extends Component {
addClick=()=>{
const nameValue=this.nameNode.value
const ageValue=this.ageNode.value
const personObj={id:nanoid(),name:nameValue,age:ageValue}
this.props.jiayiren(personObj)
this.nameNode.value=""
this.ageNode.value=""
}
render() {
console.log(this.props.yiduiren,"gggg")
return (
<div>
<h1>上方显示的和为{this.props.sum}</h1>
<input type="text" placeholder="请输入姓名" ref={c=>this.nameNode=c}/>
<input type="text" placeholder="请输入年龄" ref={c=>this.ageNode=c}/>
<button onClick={this.addClick}>添加</button>
<ul>
{
this.props.yiduren.map(p=>{
return <li key={p.id}>姓名:{p.name}===年龄:{p.age}</li>
})
}
</ul>
</div>
)
}
}
export default connect(
state=>(
{
yiduren:state.rens,
sum:state.he
}),{
jiayiren:createPerson
}
)(index)
7.5.3 index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './redux/store'
ReactDOM.render(
// React.StrictMode检查app和app包裹的组件是否合理
<Provider store={store}>
<App />
</Provider>
,
document.getElementById('root')
);
7.5.4 app.js
import Login from './components/Login'
import Register from "./components/Register"
function App() {
return (
<div>
<Login/>
<hr/>
<Register></Register>
</div>
);
}
export default App;