本篇主要将react全家桶的产品很是精炼的提取了核心内容,精华程度堪比精油。各位大人,既然来了,客官您坐,来人,给客官看茶~~javascript
首先,本篇文章要求您对js,react等知识有必定的了解,若是未曾了解,建议您先看一下:React精髓!一篇全归纳(急速) java
React有props和state:react
这就意味着若是是一个数据状态很是复杂的应用,更多的时候发现React根本没法让两个组件互相交流,使用对方的数据,react的经过层级传递数据的这种方法是很是难受的,这个时候,迫切须要一个机制,把全部的state集中到组件顶部,可以灵活的将全部state各取所需的分发给全部的组件,是的,这就是reduxajax
combineReducers()
上述步骤,对应的序号,我会在相关代码标出npm
npm install redux -S // 安装
import { createStore } from 'redux' // 引入
const reducer = (state = {count: 0}, action) => {----------> ⑴
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
const actions = {---------->⑵
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
const store = createStore(reducer);---------->⑶
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
复制代码
本身画了一张很是简陋的流程图,方便理解redux的工做流程redux
刚开始就说了,若是把store直接集成到React应用的顶层props里面,只要各个子组件能访问到顶层props就好了,好比这样:api
<顶层组件 store={store}>
<App />
</顶层组件>
复制代码
不就ok了吗?这就是 react-redux。Redux 官方提供的 React 绑定库。 具备高效且灵活的特性。promise
看我上边那个代码的顶层组件4个字。对,你没有猜错。这个顶级组件就是Provider,通常咱们都将顶层组件包裹在Provider组件之中,这样的话,全部组件就均可以在react-redux的控制之下了,可是store必须做为参数放到Provider组件中去bash
<Provider store = {store}>
<App />
<Provider>
复制代码
这个组件的目的是让全部组件都可以访问到Redux中的数据。
复制代码
这个才是react-redux中比较难的部分,咱们详细解释一下服务器
首先,先记住下边的这行代码:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
复制代码
这个单词翻译过来就是把state映射到props中去 ,其实也就是把Redux中的数据映射到React中的props中去。
举个栗子:
const mapStateToProps = (state) => {
return {
// prop : state.xxx | 意思是将state中的某个数据映射到props中
foo: state.bar
}
}
复制代码
而后渲染的时候就可使用this.props.foo
class Foo extends Component {
constructor(props){
super(props);
}
render(){
return(
// 这样子渲染的其实就是state.bar的数据了
<div>this.props.foo</div>
)
}
}
Foo = connect()(Foo);
export default Foo;
复制代码
而后这样就能够完成渲染了
这个单词翻译过来就是就是把各类dispatch也变成了props让你能够直接使用
const mapDispatchToProps = (dispatch) => { // 默认传递参数就是dispatch
return {
onClick: () => {
dispatch({
type: 'increatment'
});
}
};
}
复制代码
class Foo extends Component {
constructor(props){
super(props);
}
render(){
return(
<button onClick = {this.props.onClick}>点击increase</button>
)
}
}
Foo = connect()(Foo);
export default Foo;
复制代码
组件也就改为了上边这样,能够直接经过this.props.onClick,来调用dispatch,这样子就不须要在代码中来进行store.dispatch了
react-redux的基本介绍就到这里了
若是按照原始的redux工做流程,当组件中产生一个action后会直接触发reducer修改state,reducer又是一个纯函数,也就是不能再reducer中进行异步操做;
而每每实际中,组件中发生的action后,在进入reducer以前须要完成一个异步任务,好比发送ajax请求后拿到数据后,再进入reducer,显然原生的redux是不支持这种操做的
这个时候急需一个中间件来处理这种业务场景,目前最优雅的处理方式天然就是redux-saga
redux-saga提供了一些辅助函数,用来在一些特定的action 被发起到Store时派生任务,下面我先来说解两个辅助函数:takeEvery
和 takeLatest
takeEvery就像一个流水线的洗碗工,过来一个脏盘子就直接执行后面的洗碗函数,一旦你请了这个洗碗工他会一直执行这个工做,绝对不会中止接盘子的监听过程和触发洗盘子函数
例如:每次点击 按钮去Fetch获取数据时时,咱们发起一个 FETCH_REQUESTED 的 action。 咱们想经过启动一个任务从服务器获取一些数据,来处理这个action,相似于
window.addEventLister('xxx',fn)
复制代码
当dispatch xxx的时候,就会执行fn方法,
首先咱们建立一个将执行异步 action 的任务(也就是上边的fn):
// put:你就认为put就等于 dispatch就能够了;
// call:能够理解为实行一个异步函数,是阻塞型的,只有运行完后面的函数,才会继续往下;
// 在这里能够片面的理解为async中的await!但写法直观多了!
import { call, put } from 'redux-saga/effects'
export function* fetchData(action) {
try {
const apiAjax = (params) => fetch(url, params);
const data = yield call(apiAjax);
yield put({type: "FETCH_SUCCEEDED", data});
} catch (error) {
yield put({type: "FETCH_FAILED", error});
}
}
复制代码
而后在每次 FETCH_REQUESTED action 被发起时启动上面的任务,也就至关于每次触发一个名字为 FETCH_REQUESTED 的action就会执行上边的任务,代码以下
import { takeEvery } from 'redux-saga'
function* watchFetchData() {
yield* takeEvery("FETCH_REQUESTED", fetchData)
}
复制代码
注意:上面的 takeEvery 函数可使用下面的写法替换
function* watchFetchData() {
while(true){
yield take('FETCH_REQUESTED');
yield fork(fetchData);
}
}
复制代码
在上面的例子中,takeEvery 容许多个 fetchData 实例同时启动,在某个特定时刻,咱们能够启动一个新的 fetchData 任务, 尽管以前还有一个或多个 fetchData 还没有结束
若是咱们只想获得最新那个请求的响应(例如,始终显示最新版本的数据),咱们可使用 takeLatest 辅助函数
import { takeLatest } from 'redux-saga'
function* watchFetchData() {
yield* takeLatest('FETCH_REQUESTED', fetchData)
}
复制代码
和takeEvery不一样,在任什么时候刻 takeLatest 只容许执行一个 fetchData 任务,而且这个任务是最后被启动的那个,若是以前已经有一个任务在执行,那以前的这个任务会自动被取消
redux-saga框架提供了不少建立effect的函数,下面咱们就来简单的介绍下开发中最经常使用的几种
take函数能够理解为监听将来的action,它建立了一个命令对象,告诉middleware等待一个特定的action, Generator会暂停,直到一个与pattern匹配的action被发起,才会继续执行下面的语句,也就是说,take是一个阻塞的 effect
用法:
function* watchFetchData() {
while(true) {
// 监听一个type为 'FETCH_REQUESTED' 的action的执行,直到等到这个Action被触发,才会接着执行下面的 yield fork(fetchData) 语句
yield take('FETCH_REQUESTED');
yield fork(fetchData);
}
}
复制代码
put函数是用来发送action的 effect,你能够简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect
用法:
export function* toggleItemFlow() {
let list = []
// 发送一个type为 'UPDATE_DATA' 的Action,用来更新数据,参数为 `data:list`
yield put({
type: actionTypes.UPDATE_DATA,
data: list
})
}
复制代码
call函数你能够把它简单的理解为就是能够调用其余函数的函数,它命令 middleware 来调用fn 函数, args为函数的参数,注意: fn 函数能够是一个 Generator 函数,也能够是一个返回 Promise 的普通函数,call 函数也是阻塞 effect
用法:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
export function* removeItem() {
try {
// 这里call 函数就调用了 delay 函数,delay 函数为一个返回promise 的函数
return yield call(delay, 500)
} catch (err) {
yield put({type: actionTypes.ERROR})
}
}
复制代码
fork 函数和 call 函数很像,都是用来调用其余函数的,可是fork函数是非阻塞函数,也就是说,程序执行完 yield fork(fn, args)
这一行代码后,会当即接着执行下一行代码语句,而不会等待fn函数返回结果后,在执行下面的语句
用法:
import { fork } from 'redux-saga/effects'
export default function* rootSaga() {
// 下面的四个 Generator 函数会一次执行,不会阻塞执行
yield fork(addItemFlow)
yield fork(removeItemFlow)
yield fork(toggleItemFlow)
yield fork(modifyItem)
}
复制代码
select 函数是用来指示 middleware调用提供的选择器获取Store上的state数据,你也能够简单的把它理解为redux框架中获取store上的 state数据同样的功能 :store.getState()
用法:
export function* toggleItemFlow() {
// 经过 select effect 来获取 全局 state上的 `getTodoList` 中的 list
let tempList = yield select(state => state.getTodoList.list)
}
复制代码
**index.js **
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
import Counter from './Counter'
import rootReducer from './reducers'
const sagaMiddleware = createSagaMiddleware() // 建立了一个saga中间件实例
// 下边这句话和下边的两行代码建立store的方式是同样的
// const store = createStore(reducers,applyMiddlecare(middlewares))
const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore)
const store = createStoreWithMiddleware(rootReducer)
sagaMiddleware.run(rootSaga)
const action = type => store.dispatch({ type })
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
}
render()
store.subscribe(render)
复制代码
sagas.js
import { put, call, take,fork } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga'
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
function* incrementAsync() {
// 延迟 1s 在执行 + 1操做
yield call(delay, 1000);
yield put({ type: 'INCREMENT' });
}
export default function* rootSaga() {
// while(true){
// yield take('INCREMENT_ASYNC');
// yield fork(incrementAsync);
// }
// 下面的写法与上面的写法上等效
yield* takeEvery("INCREMENT_ASYNC", incrementAsync)
}
复制代码
reducer.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
case 'INCREMENT_ASYNC':
return state
default:
return state
}
}
复制代码
从上面的代码结构能够看出,redux-saga的使用方式仍是比较简单的,相比较以前的redux框架的CounterApp,多了一个sagas的文件,reducers文件仍是以前的使用方式
ok,故事到这里就接近尾声了,以上主要介绍了redux,react-redux和redux-saga目前redux全家桶主流的一些产品,接下来,主要会产出一下根据源码,手写一下redux和react-redux的轮子