前言
-
Redux是为javascript应用程序提供一个可预测(根据一个固定的输入,必然会得到一个固定的结果)的状态容器。可以运行于服务端,客户端,原生应用,从Flux演变而来。简单容易上手
-
集中的管理react中多个组件的状态
-
redux是专门作状态管理的js库,并不是react的插件库,也可以用在其他js框架中,例如vue,但是基本用在react中
-
可以同一个地方查询状态,改变状态,传播状态,用在中大项目。如下场景:组件状态需要共享,在任何地方都可以拿到,组件需要改变全局状态,一个组件需要改变另外一个组件的状态。创建store实例,其它组件导入并共享这个store实例
一、redux
1.redux三大原则
1)、单一数据源
2)、State 是只读的
3)、使用纯函数来执行修改
2.redux的安装
安装稳定版:
npm install --save redux
yarn add redux
附加包:多数情况下,你还需要使用 React 绑定库和开发者工具。
npm install --save react-redux
npm install --save-dev redux-devtools
3.redux成员
4.store成员
5.数据流动
6.redux操作流程
//安装:
yarn add redux
//一、创建reducer
// 参数:
// state:原始的state
// action:要做的事情,动作的类型
// 返回值:必须要有,是新的state(修改后的state)。getState()函数会调用reducer
// reducer要求是个纯函数(在函数内部不能修改函数的参数(输入),要有返回值)
//通过组件修改store中的state(数据)),修改store中的state(数据)后,还需要把数据响应到组件上,就需要使用 subscribe。
//它的功能是:传入旧的state,根据action对state做操作,返回新的state。
import {createStore} from 'redux'
const reducer = (state,action)=>{
let {type,payload}=action
swtich (type){
case XXXXX :{
//数据的逻辑处理
return {
...state,
属性名:新的值
}
}
default:
return state
}
}
//二、创建state对象
// 仓库里的数据
export default {
count:0
}
//三、创建store对象(仓库)
//使用createStore(对仓库的操作,仓库的数据)
//创建仓库时,需要说清楚仓库中存储的数据(state),以及对数据的操作(reducer)
store = createStore(reducer,state)
export default store;
//四、在组件内部使用仓库(如:获取仓库的数据,修改仓库的数据,添加,删除)
import store from '...'
store.getState() //获取状态,执行一次
store.dispatch({type:xxx,payload:ooo}) //发送action给reducer type是必传参数
store.subscribe(回调) //订阅 state 更新state时触发
7.action里处理异步
需要安装中间件 redux-thunk ,redux-thunk可以增强dispatch的功能,让dispatch可以接受一个函数作为参数。
./src/plugins/redux.js
//安装中间件改装 redux
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
let store = createStore(reducer,state,applyMiddleware(thunk));
./src/store/actionCreators.js
//处理异步
export let ADD =()=>((dispatch)=>{
axios({
url:"http://localhost:3000/inc",
})
.then(res=>{
dispatch({
type:"INCREMENT",
payload:res.data.num
})
})
})
App组件
./src/App.js
import { increment,decrement,ADD } from "./store/actionCreators";
store.dispatch(ADD());
8.combineReducers提取reducer
当应用逻辑逐渐复杂的时候,我们就要考虑将巨大的 Reducer 函数拆分成一个个独立的单元,这在算法中被称为 ”分而治之“,拆分后的reducer 在 Redux 中实际上是用来处理 Store 中存储的 State 中的某一个数据,一个 Reducer 和 State 对象树中的某个属性对应。
把一个大的reducer拆成若干个小的。注意:把大的state也分开,并且放在每个reducer。也就是说,每个reducer里面写上它要操作的数据,这样的话,在一个reducer里包含了,数据(state)和数据的操作(reducer)。
// ./src/plugins/myRedux.js
import {createStore,applyMiddleware,combineReducers} from 'redux'
import thunk from 'redux-thunk'
import count from "../store/reducers/count";
import todos from "../store/reducers/todos";
let rootReducer = combineReducers({
todos:todos,
count:count
});
//去掉了第二个参数state(因为state拆分到了每个reducer里了)
export default createStore(rootReducer,applyMiddleware(thunk));
// ./src/store/reducers/count.js
// 是当前reducer要操作的数据
let initCount = 8;
const count = (count=initCount,action)=>{
switch(action.type){
case "INCREMENT":{
return count+action.payload;
}
case "DECREMENT":{
return count-action.payload;
}
default: return count;
}
}
export default count;
// ./src/store/reducers/todos
let initState=[] //当前reducer所操作的数据,放在里自己的模块里。
const todos = (todos, action) => {
switch (action.type) {
case "ADD_TODO": {
return [
...todos,
{
id: action.id,
text: action.text,
completed: false
}
]
}
case "REMOVE_TODO": {
const { id } = action;
todos.map((item,index) => item.id ===id && todos.splice(index, 1));
return [...todos]
}
case "CHECK_TODO": {
const { id } = action;
todos.map((item,index) => item.id ===id && (todos[index].completed=!todos[index].completed));
return [...todos]
}
default:
return todos;
}
};
export default todos;
//删除掉state文件。
//组件里:
写法基本上不变
store.getState().todos
state数据不写在类内部订阅,可以写在主入口文件 订阅store数据的更新
二、react-redux
1.redux里出现的问题:
1)、组件中出现了大量的store对象
2)、在redux里,凡是使用state里数据的组件,必须加上 store.subscribe() 函数,否则,数据不是响应式的
2. react-redux做了哪些事情?
基于redux思想,专门为react使用redux而生,react-redux是连接redux和react组件的桥梁
3. 安装
npm install --save react-redux
4. react-redux的API
1)、组件:可以让组件拿到state
即不需要使用传统的subscribe()来监听state重绘组件
import {Provider} from "react-redux";
import store from './redux/store'
ReactDOM.render((
<Provider store={store}>
<App/>
</Provider>
), document.getElementById('root'));
2)、connect(): 链接 ,(返回值)是个高阶组件,用来链接react组件和redux
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
功能:把store和react组件联系在一起。只要store发生了变化就会调用mapStateToProps方法。Connect方法就是个高阶组件。
参数1:mapStateToProps函数
功能: 把仓库里的state合并到props里。给mapStateToProps函数传入所有state,它返回指定的state数据(需要合并到组件props中的state)。返回的state与组件的 props 合并。
所以,当store发生变化时,mapStateToProps方法就会更新组件里的props,那么组件就更新了(因为props变了)。
参数:state:所有的state
返回值:指定的state(组件里需要的state)。
实例代码:
const mapStateToProps = (state)=>{
return {
count:state.count
}
}
参数2:mapDispatchToProps函数
功能:
把dispatch和props联系起来。传入dispatch,返回绑定好的action方法。
更改数据必须要触发action,所以,mapDispatchToProps把 action 作为组件的props 进行绑定(联系的体现),要派发的函数名,可以是多个函数。mapDispatchToProps 就是用于建立组件跟store.dispatch(是action)的映射关系。
参数:
dispatch: 派发
**ownProps:**当前组件的props,即使用标签时,传入的props
返回值:
对象:表示所有dispatch的对象
**示例代码:**
import React from "react";
import './App.css';
import {ADD,REDUCE} from "./store/action";
import {connect} from "react-redux";
class App extends React.Component {
add(){
this.props.add();
}
reduce(){
this.props.reduce();
}
render = () => {
return (
<div className="App">
<p>{this.props.count}</p>
<input type="button" value=" 加 " onClick={()=>this.add()} />
<input type="button" value=" 减 " onClick={()=>this.reduce()} />
</div>
)
}
}
export default connect((state)=>{
return {
count:state.count
}
},dispatch=> ({
add: () => dispatch(ADD()),
reduce: () => dispatch(REDUCE())
}))(App);
5. react-redux的实现
安装:npm install --save react-redux
//1、主入口文件 index.js
import {Provider} from 'react-redux'
import store from './plugins/redux'
<Provider store={store}>
<App/>
</Provider>
//2、容器组件里:App组件
import {connect} from "react-redux";
class App extends React.Component {
add(){
//直接用props来调用dispatch,而不需要store
this.props.dispatch({
type:"INCREMENT",
payload:2
});
}
render = () => (
<div className="App">
<p>{this.props.count}</p> // 使用props可以直接拿到state里的数据,而不需要store
<input type="button" value=" 加 " onClick={()=>this.add()} />
</div>
)
}
//容器组件对外开放时,(把redux里的state转到props)
export default connect((state)=>{
return {
count :state.count
}
})(App);
三、react-redux、redux和vuex的区别