Redux
由Flux演变而来,作为状态容器,提供可预测的状态管理。
跨组件,多层级组件,想要 需要共享的数据,可以使用。
能不用尽量不用。
redux
库可以脱离 React 应用使用。
安装Redux- DevTools
chrome 商店 收索 redux dev 即可安装。
安装之后还不可以使用。
创建 store 时,添加以下代码 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
let store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
基本概念
state
State 是只读的。
Store对象包含所有数据。由 store 管理且由 getState()
方法获得。它表示了 Redux 应用的全部状态。
state 可以是任意的数据类型。然而你应尽可能确保 state 可以被序列化,而且不要把什么数据都放进去,导致无法轻松地把 state 转换成 JSON。
Action
Action是一个用来描述修改 state 的操作。通过 dispatch 告诉 store , state 如何修改。
action只是一个传递信息的,他不能改变store。store 只能被 reducer改变。
action
必须有一个type属性,表明即将执行的action的类型,type通常被定义为一个字符常量。action对象中的其他属性是携带的信息。
const action = {
type: 'ADD_TODO',
payload: 1
};
上面代码中,Action 的名称是ADD_TODO,它携带的信息是number: 1。
Action Creator
action creator是创建action的函数的工厂,简单地返回action,它能根据传入的参数设置action的属性值。
const ADD_TODO = '添加 TODO';
function addTodo(number) {
return {
type: ADD_TODO,
number
}
}
const action = addTodo(3);
Reducer
当store 收到 Action 以后,必须给出一个新的 State。这种 State 的计算过程就叫做 Reducer。
它要做的仅仅是 —— 负责初始 state,当 store 收到 Action 以后,根据 state 和 action 返回新的 state。这种 State 的计算过程就叫做 Reducer。
可以理解为一个专门处理state的工厂 给他一个旧数据它会根据不同action.type
返回新的数据 也就是:旧state + action = 新state,每个项目有且可以有多个reducer。
Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
纯函数:
- 传入相同的参数会返回相同的结果。
- 执行纯函数不会造成副作用。(这里的副作用指的是函数改变了作用域之外的状态值,比如函数外部定义了变量a,在函数内部改变了变量a的值。)
可用 immutable.js
来保证数据的不可变性
/*
根据传入的 action 的 type 不同,返回一个新的 state 数据
*/
// 先初始化 state
const initCounter = 0;
const reducer = function (state = initCounter, action) {
const { number } = action;
let data = {...state};
switch (action.type) {
case 'ADD_NUMBER':
return data + number
default:
return state
}
};
reducer 函数 不是修改 state
,而是 深度拷贝 state
得到新对象,来操作这个新对象并当作 新的 state 返回。
使用 ES7 的{ ...state }
或Object.assign({},state,{})
实现浅拷贝满足使用JSON.parse(JSON.stringify(state)) 对象的深度拷贝
在
default
情况下返回旧的state
。遇到未知的 action 时,一定要返回旧的state
。
combineReducers
合并 多个 reducer 的方法。
一个 reducer 处理 同一个 state,不可避免的 导致 state 过于庞大 和 reducer 方法臃肿。
把 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state
的一部分。
combineReducers
辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore
方法。
let data = {
'1': {
arr:[1,2,3],
number:1,
},
'2':{
msg: 'hello'
}
}
let reducerOne = (state=data[1],action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_NUMBER':
data.number += action.number;
return data;
case 'ADD_ARR':
data.arr.push(action.number);
return data;
default:
return data;
}
}
let reducerTwo = (state=data[2],action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_STRING':
data.msg += action.msg;
return data;
default:
return data;
}
}
let reducer = combineReducers({
reducerOne,reducerTwo
});
/*
当你触发 action 后,combineReducers 返回的 reducer 会负责调用reducerOne,reducerTwo
*/
const store = createStore(reducer);
console.log(store.getState())
/*
{reducerTwo: {…}, reducerOne: {…}}
reducerOne: {arr: Array(3), number: 1}
reducerTwo: {msg: "hello"}
__proto__: Object
*/
dispatch
dispatch 是一个接收 action 的函数,往 store 分发一个或多个 action
,要么不分发任何 action。
store.dispatch({
type: 'ADD_TODO',
number: 5
});
// 或者
store.dispatch(addTodo(5));
createStore
创建store 的方法。
createStore(reducer, preloadedState?, enhancer?)
- 上面的 reducer 函数,必须有.
- preloadedState 可选参数,初始时的 state。 如果你使用
combineReducers
创建reducer
,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何reducer
可理解的内容。 - enhancer 可选参数,store的增强器,顾名思义,就是增强store的功能。比如 使用第三方插件,react-thunk
let reducerOne = (state={},action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_NUMBER':
data.number += action.number;
return data;
default:
return data;
}
}
let reducerTwo = (state={},action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_STRING':
data.msg += action.msg;
default:
return data;
}
}
/*
combineReducers 创建的reducer,在createStore 中初始化 state 时,
属性值 是 变量时,必须重新起属性名,属性值和属性名不能相同。
像这样 是不能正常初始化 state
let reducer = combineReducers({
reducerOne,reducerTwo
});
let data = {
'reducerOne':{
msg: 'hello'
},
'reducerTwo': {
arr:[1,2,3],
number:1,
},
}
*/
let reducer = combineReducers({
'one':reducerOne,
'two':reducerTwo
});
let data = {
'one':{
msg: 'hello'
},
'two': {
arr:[1,2,3],
number:1,
},
}
const store = createStore(reducer,data);
store
Store 就是保存数据的地方,一个仓库。整个应用只能有一个 Store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合而不是创建多个 store。
Store将actions和reducers结合起来,store的功能是:
- 维持应用的 state
- 通过
getState()
拿到state。 - 通过
dispatch(action)
更新state。 -
通过
subscribe(listener)
注册监听器。listener是一个函数,当发送action的时候会执行listener。每次state变更时,都会触发其订阅的事件 listener。
- 执行
subscribe(listener)
会返回 一个函数,调用这个函数能够注销监听器。
通过 创建一个 store,传入 reducer,每当我们在 store 上 dispatch 一个 action,reducer 会根据 action的 type ,做修改 state 的操作,store 内的数据就会相应地发生变化。
// 创建 一个 store,传入 reducer
const store = createStore(reducer);
// 组件中 注册 subscribe(listener) 绑定 用于 setState 的函数
class App extends React.Component{
constructor(props) {
super(props);
this.unsubscribe = store.subscribe(this.changeStore);
}
state = {
num: store.getState().reducerOne.number,
}
changeStore = ()=>{
this.setState({num:store.getState().reducerOne.number});
}
componentWillUnmount() {
this.unsubscribe(); //注销监听器
}
handClick = () =>{
store.dispatch({
type:'ADD_NUMBER',
number: 1,
})
}
render() {
return <div>
<button onClick={this.handClick}>点击</button>
<p>{this.state.num}</p>
</div>
}
}
ReactDOM.render(<App />,document.getElementById('app'))
Redux-thunk --- 中间件(middleware)
中间件就是一个函数,对store.dispatch
方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
也就是 改变action -> reducer 的过程。变为 action -> middlewares(中间件) -> reducer 。使用它改变数据流,实现异步 action .
Action 发出以后,Reducer 立即算出 State,这叫做同步。Action 发出以后,过一段时间再执行 Reducer,这就是异步。
redux-thunk
中间件改造了redux的dispatch方法允许我们用store.dispatch(fn)
, fn
可以是一个函数。而且此函数可以接受两个参数:dispatch
、getState
做为参数。
也就是store.dispatch()
和store.getState()
的封装
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
// reducer
let data = {};
let reduce = (state=data,action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'GET_LIST':
data.list = action.list;
return data;
default:
return data;
}
};
// 解决redux 和 redux devtools 的冲突问题
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
// 加载 redux-thunk
const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer,enhancer);
//action
function incrementIfOdd() {
return async (dispatch, getState) => {
let list = await fetch(
'http://rap2.taobao.org:38080/app/mock/254619/redux-thunk'
).then(res => res.json());
dispatch({
type: 'GET_LIST',
list
});
console.log(getState());
/*
{
arr: (3) [1, 2, 3]
msg: "hello"
}
*/
}
}
React-Redux
react-redux是Redux 的作者封装了一个 React 专用的库 React-Redux 。
Redux 本身和React没有关系,只是数据处理中心,是React-Redux让他们联系在一起。
这个库是可以选用的。实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范。
React-Redux 将所有组件分成两大类:UI 组件和容器组件
- 前者会处理逻辑
- 后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控
两个核心概念
Provider
的组件
将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去。
<Provider store = {store}>
<App />
<Provider>
通过用 Provider
组件包装整个应用,App 组件的所有子组件都可以访问 Redux store。
connect(stateProps, dispatchProps)(ComponentName)
映射器,在需要用到 state 或 dispatch(action)
的组件中使用 connect 函数( 即高阶组件) 进行包装。
可以看到connect(stateProps, dispatchProps)(ComponentName)
调用了两次。
其实connect
是一个高阶函数,它简单说就是当你调用它时会返回一个函数。然后调用返回的函数传入一个组件时,它会返回一个新(包装的)组件。
stateProps(state, ownProps?)
它是个自定义函数,从Redux 状态树中提取需要的state 作为props传递给当前的组件。
所以他的作用就是其实也就是当 redux 的state 改变,props 改变重新 渲染依赖组件。
stateProps
结果一定要返回一个object 。
- 第一个参数
state
, 相当于store.getState()
的封装,但是 你可以只获取 state 中的任意一个或多个数据。 - 第二个可选 参数
ownProps
,父组件 传递给 组件 的 props。
dispatchProps(dispatch, ownProps?)
把 组件内 分发 action 的方法 当作 props 。
- dispatch参数: 把
store.dispatch()
方法封装,直接调用 dispatch。 - ownProps,父组件 传递给 组件 的 props。
function Bt(props) {
return (
<div>
<button onClick={props.handClick}>点击</button>
<p>{props.number}</p>
</div>
)
}
let stateProps = state => {
return {
number: state.number
}
}
let dispatchProps = dispatch =>{
return {
handClick: e => dispatch({
type:'ADD_NUMBER',
number: 1,
})
}
}
// connect()() 返回的是一个组件
let Button = connect(stateProps,dispatchProps)(Bt);
class App extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<Provider store = {store}>
<Button></Button>
</Provider>
)}
}
ReactDOM.render(<App/>,document.getElementById('app'))
Redux系列之分析中间件原理(附经验分享)
Redux 中文文档
Redux 入门到高级教程
[译] 2019 React Redux 完全指南
react-redux一点就透,我这么笨都懂了!
React Redux
一篇文章总结redux、react-redux、redux-saga
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。