当项目越来越复杂时,组件越来越多的时候,组件之间数据的共享就成为了一个问题,那么Redux就是用来解决各个组件之间数据共享的问题的。
redux在vue,node,react都可以使用
action:官方的解释是action是把数据从应用传到 store 的有效载荷,它是 store 数据的唯一来源;要通过本地或远程组件更改状态,需要分发一个action;
reducer:action发出了做某件事的请求,只是描述了要做某件事,并没有去改变state来更新界面,reducer就是根据action的type来处理不同的事件;
store:store就是把action和reducer联系到一起的对象,store本质上是一个状态树,保存了所有对象的状态。任何UI组件都可以直接从store访问特定对象的状态。
在Redux中,所有的数据(比如state)被保存在一个被称为store的容器中 ,在一个应用程序中只能有一个store对象。当一个store接收到一个action,它将把这个action代理给相关的reducer。reducer是一个纯函数,它可以查看之前的状态,执行一个action并且返回一个新的状态。
Redux工作流
React Components 借书的用户
Action Creators “我要借XX书”
Store 图书管理员
Reducers 借阅历史记录本
设计思想
视图和状态是一一对应的
所有的状态,都保存在一个对象里面
纯函数底层实列计数器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="store.dispatch({type:'COUNT_DECRMENT',num:1})">-</button>
<h1 id="count"></h1>
<button onclick="store.dispatch({type:'COUNT_INCRMENT',num:1})">+</button>
<!-- 点击加减首先调用store里面的dispathch的方法 -->
<script>
const createStrore = (reducer) => {
//集中管理state,dispatch
let state=null
const getState = () => state;
const listeners = [];
const subscribe = (listener) => listeners.push(listener)
const dispatch = (action) => {
state=changeState(state, action)//运算执行
listeners.forEach((listener) => listener())
}
dispatch({})
//定义一个监听器,用于管理所有定义的方法
return {
getState,dispatch,subscribe
}
}
const contState = {
count: 0,
}
const changeState = (state,action) => {
if(!state) return contState
switch (action.type) {
case "COUNT_DECRMENT":
return {
count:state.count - action.num,
}
case "COUNT_INCRMENT":
return {
count:state.count + action.num,
}
default:
return state
}
}
const store = createStrore(changeState)//初始进来执行store里面的
const render = () => {
document.getElementById('count').innerHTML = store.getState().count
}
render()//初始化渲染contState下的count
store.subscribe(render)//类似更新数据
</script>
</body>
</html>
在 react 中使用 redux
react-redux提供两个核心的api
Provider:提供store,根据单一store的原则,一般只出现在顶层
connect:用于连接展示组件和React store
Redux 不支持多个 store。相反,只有一个单一的store和一个根级的reduce函数(reducer)。随着应用不断变大,你应该把根级的reducer拆成多个小的 reducers,分别独立地操作state树的不同部分,而不是添加新的stores。这就像一个 React 应用只有一个根级的组件,这个根组件又由很多小组件构成。
不同的业务模块有自己的reducers,使用 combineReducers 方法将所有的reducer整合到一起。
react-redux 实现一个简单的计数器功能
安装依赖:在终端中执行下面的命令,安装 redux 和 react-redux
yarn add redux react-redux -S
store.js文件
import {createStore} from 'redux'
//解构出redux中的createStore方法
/**
* reducer是形式为(state,action)=>state 的纯函数
* 在reducer中对state设置默认值
* reducer的作用是通过判断action传过来的值,对数据进行操作
* 当state变化后,需要返回一个新的state,而不是修改传入的参数
**/
import reducer from'./reducer'
const store=createStore(reducer)
export default store;
reducer.js
reducer 最终返回一个新的state,当 state 被更改后,store.subscribe 方法里的回调函数就会执行,会通知 view 去重新获取 state,做视图的更新;
const defaultState={
count:1
}
const reducer=(state=defaultState,action)=>{
switch(action.type){
case 'inc':
return {
count:state.count+action.num
}
case 'dec':
return {
count:state.count-action.num
}
default:
return state;
}
}
export default reducer;
connect.js
//帮我们把redux和展示组件做关联,将store里的属性和方法挂载到组件里
//格式为connect(mapStateToProps, mapDispatchToProps)
// 将第一个参数中返回的属性转化为展示组件props上的属性;
// 将第二个参数中返回的方法转化为展示组件props上的方法;
// 如果只想获取到方法,在第一个参数传null;
import {connect} from 'react-redux'
const mapState=(state)=>{
return {
count:state.count
}
}
const mapDispatch=(dispatch)=>{
return {
increm:(data)=>{
dispatch({type:'inc',num:2})
},
increm:()=>{
dispatch({type:'inc',num:2})
}
}
}
connect(mapState,mapDispatch)
Count.jsx
import React, { Component } from 'react'
import Button from './Button'
import connect from './connect'
class Count extends Component {
render() {
return (
<div>
<Button type='inc'>+</Button>
<span>{this.props.count}</span>
<Button type='dec'>-</Button>
</div>
)
}
}
export default connect(Count)
//使用 connect 对当前组件做一个增强,Count 组件里就可以通过 this.props 读取到 store 中的数据;
Button.jsx
// 使用 connect 对当前组件做一个增强,Button 组件里通过 this.props 调用 connect 中的方法;
// connect 中调用 store.dispatch 方法将 action 发送到 reducer 中;
// reducer 接收到 action,对数据进行处理后,返回新的 state;
// 当 state 被更新的时候,store.subscribe 方法会执行,通知 view 去重新获取 state;
// 获取到新的 state 后,会触发 render() 重新渲染页面;
import React, { Component } from 'react'
import connect from './connect'
class Button extends Component {
handleClick = () => {
console.log(this.props)
let {type,incre,decrem}=this.props
if(type==='inc') incre(2)
else decrem()
}
render() {
return (
<button onClick={this.handleClick}>
{this.props.children}
</button>
)
}
}
export default connect(Button)
index.js
import Count from './15-redux/呆猫/Count'
//自己定义的文件名所处位置,
import {Provider} from 'react-redux'
//引入store挂载
import store from './15-redux/呆猫/store'
ReactDOM.render(
<Provider store={store}>
<Count/>
</Provider>
,
document.getElementById('root')
);