由Flux演变而来,作为状态容器,提供可预测的状态管理。
跨组件,多层级组件,想要 需要共享的数据,可以使用。
能不用尽量不用。
redux
库可以脱离 React 应用使用。
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 是只读的。
Store对象包含所有数据。由 store 管理且由getState()
方法获得。它表示了 Redux 应用的全部状态。
state 可以是任意的数据类型。然而你应尽可能确保 state 可以被序列化,而且不要把什么数据都放进去,导致无法轻松地把 state 转换成 JSON。
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的函数的工厂,简单地返回action,它能根据传入的参数设置action的属性值。
const ADD_TODO = '添加 TODO';
function addTodo(number) {
return {
type: ADD_TODO,
number
}
}
const action = addTodo(3);
当store 收到 Action 以后,必须给出一个新的 State。这种 State 的计算过程就叫做 Reducer。
它要做的仅仅是 —— 负责初始 state,当 store 收到 Action 以后,根据 state 和 action 返回新的 state。这种 State 的计算过程就叫做 Reducer。
可以理解为一个专门处理state的工厂 给他一个旧数据它会根据不同action.type
返回新的数据 也就是:旧state + action = 新state,每个项目有且可以有多个reducer。
Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
纯函数:
可用
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
。
合并 多个 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 是一个接收 action 的函数,往 store 分发一个或多个 action
,要么不分发任何 action。
store.dispatch({
type: 'ADD_TODO',
number: 5
});
// 或者
store.dispatch(addTodo(5));
创建store 的方法。
createStore(reducer, preloadedState?, enhancer?)
combineReducers
创建 reducer
,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer
可理解的内容。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。当需要拆分数据处理逻辑时,你应该使用 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
{this.state.num}
}
}
ReactDOM.render( ,document.getElementById('app'))
中间件就是一个函数,对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是Redux 的作者封装了一个 React 专用的库 React-Redux 。
Redux 本身和React没有关系,只是数据处理中心,是React-Redux让他们联系在一起。
这个库是可以选用的。实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范。
Provider
的组件将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去。
通过用
Provider
组件包装整个应用,App 组件的所有子组件都可以访问 Redux store。
connect(stateProps, dispatchProps)(ComponentName)
映射器,在需要用到 state 或 dispatch(action)
的组件中使用 connect 函数( 即高阶组件) 进行包装。
可以看到connect(stateProps, dispatchProps)(ComponentName)
调用了两次。
其实connect
是一个高阶函数,它简单说就是当你调用它时会返回一个函数。然后调用返回的函数传入一个组件时,它会返回一个新(包装的)组件。
它是个自定义函数,从Redux 状态树中提取需要的state 作为props传递给当前的组件。
所以他的作用就是其实也就是当 redux 的state 改变,props 改变重新 渲染依赖组件。
stateProps
结果一定要返回一个object 。
state
, 相当于 store.getState()
的封装,但是 你可以只获取 state 中的任意一个或多个数据。ownProps
,父组件 传递给 组件 的 props。把 组件内 分发 action 的方法 当作 props 。
store.dispatch()
方法封装,直接调用 dispatch。function Bt(props) {
return (
{props.number}
)
}
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 (
)}
}
ReactDOM.render( ,document.getElementById('app'))
Redux系列之分析中间件原理(附经验分享)
Redux 中文文档
Redux 入门到高级教程
[译] 2019 React Redux 完全指南
react-redux一点就透,我这么笨都懂了!
React Redux
一篇文章总结redux、react-redux、redux-saga