因为在react中都是按需引入,为了防止文件过大,并且redux
出现的场景不多,所以就需要自行下载,在脚手架中并未下载redux
如何下载?
yarn add redux
npm i redux
redux
呢?redux
就相当于 vuex
(如果是学习过vue的小伙伴理解起来就非常轻松啦) ,就是数据状态管理模式,当我们要做的项目中有很多组件需要共享数据时,这时候就可以用 redux
搭建。
Redux
适用场景首先明确一点,Redux
是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React
就够了
Redux
的创造者 Dan Abramov
说的
只有遇到
React
实在解决不了的问题,你才需要Redux
。
简单说,如果你的UI层非常简单,没有很多互动,Redux
就是不必要的,用了反而增加复杂性。
用户的使用方式非常简单
用户之间没有协作
不需要与服务器大量交互,也没有使用WebSocket
视图层(View
)只从单一来源获取数据
上面这些情况,都不需要使用 Redux
。
用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了WebSocket
View
要从多个来源获取数据
上面这些情况才是 Redux
的适用场景:多交互、多数据源。
从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。
某个组件的状态,需要共享
某个状态需要在任何地方都可以拿到
一个组件需要改变全局状态
一个组件需要改变另一个组件的状态
Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
如何生成Store
?
import { createStore } from 'redux';
const store = createStore(fn);
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();
State
的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
Action
是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置,社区有一个规范可以参考。
const action = {
type: 'action的名称',
payload: '要携带的信息'
};
store.dispatch()
store.dispatch()
是 View 发出 Action 的唯一方法。
import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: 'action的名称',
payload: '要携带的信息'
});
结合Action Creator
,这段代码可以改写如下。
声明:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
使用:
store.dispatch(addTodo('Learn Redux'));
Store
收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer
。
const reducer = function (state, action) {
// ...
return new_state;
};
Reducer
有两个作用:初始化状态,加工状态
Reducer
被第一次调用时,是store自动触发的传递state
是undefined一般要给默认值 0
|| null
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
const state = reducer(1, {
type: 'ADD',
payload: 2
});
上面代码中,reducer
函数收到名为ADD的 Action 以后,就返回一个新的 State,作为加法的计算结果。其他运算的逻辑(比如减法),也可以根据 Action 的不同来实现。
实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch
方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore
方法。
import { createStore } from 'redux';
const store = createStore(reducer);
上面代码中,createStore
接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch
发送过来一个新的 Action,就会自动调用 Reducer
,得到新的 State
。
为什么这个函数叫做 Reducer 呢?因为它可以作为数组的reduce
方法的参数。请看下面的例子,一系列 Action 对象按照顺序作为一个数组。
onst actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }
];
const total = actions.reduce(reducer, 0); // 3
上面代码中,数组actions
表示依次有三个 Action,分别是加0
、加1
和加2
。数组的reduce
方法接受 Reducer 函数作为参数,就可以直接得到最终的状态3
。
Reducer
函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。
fn(1) = 1 只要是同样的输入(实参),必定得到同样的输出(返回)
fn(1) = 2 这就不是纯函数
function fn(num){
return num=2 // 像这种情况就是在修改形参数据
}
纯函数是函数式编程的概念,必须遵守以下一些约束。
不得改写参数
不能调用系统 I/O 的API
不能调用Date.now()
或者Math.random()
等不纯的方法,因为每次会得到不一样的结果
由于 Reducer
是纯函数,就可以保证同样的State
,必定得到同样的 View
。但也正因为这一点,Reducer
函数里面不能改变 State
,必须返回一个全新的对象,请参考下面的写法。
// State 是一个对象
function reducer(state, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state, action) {
return [...state, newItem];
}
Store 允许使用store.subscribe
方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。
import { createStore } from 'redux';
const store = createStore(reducer);
store.subscribe(listener);
显然,只要把 View 的更新函数(对于 React 项目,就是组件的render
方法或setState
方法)放入listen
,就会实现 View 的自动渲染。
// render
store.subscribe(render(){return(...)});
// setState
store.subscribe(this.setState({}));
为什么要这样做呢?
因为react默认是检测不了store的变化的,只能通过检测state的变化来重新调用render
函数进行重新渲染页面
store.subscribe
方法返回一个函数,调用这个函数就可以解除监听。
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
);
unsubscribe();
createStore
创建仓库Action
对象,可以用函数的形式创建Action Creator
Reducer
进行数据初始化store.getState()
来获取store里存放的值store.dispatch()
来和store
接触使用Reducer
来更改state的值store.subscribe()
来检测store
值的改变,重新渲染页面