redux 官网
先来看看 React 一些特点和没有解决的问题:
- 通信:组件之间如何通信?react 采用传参,对于大应用,很不方便。
- 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等
为了解决这些问题引入 redux,前几天看见一个博客,把 redux 的原理结合动图把为什么使用 redux 讲的特别透彻。
一定要看:Redux设计思想与使用场景
同时配上这篇:redux 设计思想
总结一下就是:
-
redux 的诞生是为了给 React 应用提供「可预测化的状态管理」机制。
-
Redux 会将整个应用状态(其实也就是数据)存储到到一个地方,称为 store。
-
这个 store 里面保存一棵状态树(state tree)
-
组件改变 state 的唯一方法是通过调用 store 的 dispatch 方法,触发一个 action,这个action 被对应的 reducer 处理,于是 state 完成更新。
-
组件可以派发 (dispatch) 行为 (action) 给 store,而不是直接通知其它组件
-
其它组件可以通过订阅 store 中的状态 (state) 来刷新自己的视图
一、初识 Redux
网站重要的开门注:
redux a predictable state container for JavaScript apps.
Redux是一个可预测状态容器。
为了便于直观理解,演示先不配合 React 使用,全部在一个文件 1.js 里面写。使用 node 进行调试。
先安装 redux。
npm install --save redux
实例代码如下:
const redux = require("redux");
//①创建一个纯函数reducers。
//这个函数接收两个参数:一个 state、一个 action,并返回新的 state。
const reducers = (state = {a : 10},action)=>{
if(action.type === "ADD"){
return {
a : state.a + 1
}
}else if(action.type === "UNADD"){
return {
a : state.a - 1
}
}
return state;
}
// ②创建store
const store = redux.createStore(reducers);
// ③通过getState()方法可访问state
console.log(store.getState().a);//10
// ④通过dispatch方法{type:"ADD"}进行一次加法运算
store.dispatch({type:"ADD"});
console.log(store.getState().a);//11
// ⑤通过dispatch方法{type:"UNADD"}进行一次减法运算
store.dispatch({type:"UNADD"});
console.log(store.getState().a);//10
- redux 依赖于一个纯函数(reducer),它描述了 action 如何将 state 转为新的 state,action 只说明了什么事情发生,但是不会描述 state 如何改变。绝对不能改变 state 对象,如果你要变,只能返回新的 state。什么是纯函数?不改变传入的参数的函数就是纯函数。
- redux 通过 createStore 让 store 和 reducers 产生联系。
- 产生联系的 store ,提供一个
getState()方法来访问纯函数的 state,通过dispatch({"type":...})来检测命令。唯一能够改变 state 的方法就是 dispatch 一个 action。store 就是统一管理 reducer 和 action 的,将所有的一切统一起来的。 - store 有三个主要的功能:
- 持有 app 的 state
- 允许通过 getState() 得到 state
- 允许通过 dispatch 来改变 state
注意的是:可以有多个纯函数,但只能有 1 个 store 。
- 我们知道 action 是 store 唯一的信息的来源且 action 需要被 dispatch() 函数派发,action 是纯的、扁平的 JavaScript 对象。
如果需要多个纯函数,则需要引入,combineReducers() 这个方法进行纯函数合并。
const redux = require("redux");
//①创建一个纯函数reducers。
const reducers = (state = {a : 10},action)=>{
if(action.type === "ADD"){
return {
a : state.a + 1
}
};
return state;
}
//⑥创建另一个纯函数reducers2。
const reducers2 = (state = {a : 100},action)=>{
if(action.type === "LOAD"){
return {
a : action.palyload
}
}
return state;
}
// ⑦多个需求只能拆分纯函数reducer,通过combineReducers合并成一个
const reducer = redux.combineReducers({
reducers,
reducers2
})
// ②创建store
const store = redux.createStore(reducer);
// ③通过getState()方法可访问state的加命名空间
console.log(store.getState().reducers.a);//10
// 但是通过dispatch却不用添加命名空间
store.dispatch({type:"ADD"});
console.log(store.getState().reducers.a);//11
//<============华丽的分割线===========>
// ③通过getState()方法可访问state的加命名空间
console.log(store.getState().reducers2.a);//100
// action可通过playload携带负载参数
store.dispatch({type:"LOAD",palyload:"我是携带的负载!"});
console.log(store.getState().reducers2.a);//我是携带的负载
纯函数里面的 action 就是由 type 属性的组成的 JSON,但实际上不仅仅有 type 属性,还可以有其他的属性,所有其他的属性都叫做载荷(payload)。
二、redux 结合 React
先安装:react-redux粘合剂官网:https://react-redux.js.org/
npm install --save react-redux
梳理一下 redux 和 react-redux 提供各自提供的 API。
- Redux 是可预测状态容器(reducer、state、store、dispatch、action、applyMiddleware 等API)
- react-redux 提供 Provider 和 connect 。
再看项目主要目录结构:
┣✈ main.js
┣✈ index.html
┣✈ webpack.config.js
┣✈ package.json
┗✈ views
┣✈ app
┣✈ App.js
┗✈ store
┣✈ index.js
┗✈ counterStore.js
展示项目目录代码:
- couterStore.js 放入纯函数:
export default (state = {a : 10},action)=>{
if(action.type === "ADD"){
return {
a : state.a + 1
}
}else if(action.type === "UNADD"){
return {
a : state.a - 1
}
};
return state;
}
使用 redux 第一步必须是写纯函数。
- index.js
import counterStore from "./counterStore.js";
import {combineReducers} from "redux";
export default combineReducers({
counterStore
});
使用 combineReducers 进行纯函数合并,变成一个统领文件。
- main.js
import React from "react";
import ReactDom from "react-dom";
import App from "./views/app/App.js";
import {createStore} from "redux";
import {Provider} from "react-redux";
import reducers from "./views/store";
const store = createStore(reducers);
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>
,
document.getElementById("app")
);
引入 createStore 函数、引入r educers 统领文件、创建 store、可以测试使用store.getState().a输出结果。Provider 可以让 store 照耀在 APP 里面所有的组件上,做到天下无人不识君。Provider 的机理是使用的 context 上下文机理。
- App.js
connect 这里是个高阶函数的用法,很明显 connect 进行了柯粒化处理。这里叫做装饰器。装饰器两个(mapStateToProps)(mapDispatchToProps),第一圆括号内部写如何装饰,映射全局 state 到组件的 props;第二个圆括号内部写装饰谁,将dispatch这个词语映射到 props 上。
写法一:不使用装饰器
import React,{Component} from "react";
import {connect} from "react-redux";
class App extends Component {
constructor(){
super()
}
render(){
return(
<div>
<h1>{this.props.a}</h1>
<button onClick = {this.props.add}>按我加一</button>
</div>
);
}
}
export default connect(
({counterStore})=>({
a : counterStore.a
}),
dispatch=>({
add(){
dispatch({type:"ADD"});
}
})
)(App);
写法二:装饰器写法
react 不支持装饰器写法,首先安装语法糖。
npm install --save @babel/plugin-proposal-decorators
参考 babel 官网 @babel/plugin-proposal-decorators · Babel
webpack.config.js 进行配置:
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }]
]
}
配置完,重新开启项目即可使用,改写代码:
import React,{Component} from "react";
import {connect} from "react-redux";
@connect(
({counterStore})=>({
a : counterStore.a
}),
dispatch=>({
dispatch
})
)
export default class App extends Component {
constructor(){
super()
}
render(){
return(
<div>
<h1>{this.props.a}</h1>
<button onClick = {()=>{this.props.dispatch({type:"ADD"})}}>按我加一</button>
</div>
);
}
}
两者实现的效果都是一样的。打开http://127.0.0.1:8080/,查看效果:
三、bindActionCreators(基本不用)
Action Creator 就是一个创建 action 的函数,例如:() => ({“type” : “ADD”})。不要混淆 action 和 action creator 这两个概念。Action 是一个信息的负载,而 action creator 是一个创建 action 的工厂。调用 action creator 只会生产 action,但不分发。bindActionCreators 这个 API 就是使用 Action Creator。
使用场景?所以是基本没用。
惟一会使用到
bindActionCreators的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把dispatch或 Redux store 传给它。
bindActionCreators(actionCreators, dispatch)
参数
-
actionCreators(Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。
更改 App.js
import React,{Component} from "react";
import {connect} from "react-redux";
import * as actionCreators from "./actionCreators.js";
import {bindActionCreators} from "redux";
@connect(
({counterStore})=>({
a : counterStore.a
}),
(dispatch) => ({
actionCreators: bindActionCreators(actionCreators , dispatch)
})
)
export default class App extends Component {
constructor(){
super()
}
render(){
return(
<div>
<h1>{this.props.a}</h1>
<button onClick = {this.props.actionCreators.add}>按我加一</button>
</div>
);
}
}
新增的actionCreators文件
export const add = ()=>({type:"ADD"});
加法器仍然起作用。
四、安装 redux-logger
redux-logger 是每次 dispatch 的记录器。当我们启用 logger 的时候每次 dispatch ,都会在控制台输出每次详情。而我们只需要一装一引一配。
一装 redux-logger
npm install --save redux-logger
在主入口文件 main.js 中进行 一引一配。
import React from "react";
import ReactDom from "react-dom";
import App from "./views/app/App.js";
import {createStore,applyMiddleware} from "redux";
import {Provider} from "react-redux";
import reducers from "./views/store";
import logger from "redux-logger";
const store = createStore(reducers,applyMiddleware(logger));
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>
,
document.getElementById("app")
);
输入网址:http://127.0.0.1:8080/ 查看控制台效果。









网友评论