Redux 入门
Redux 工作流程
redux是视图层框架,把所有数据都放在store之中,每个组件都要从store里拿数据,然后每个组件也能去改store里面的数据,
举例:把这个流程理解成一个图书馆的流程
-
react compontents:借书的人
-
action creators:“要借什么书”这句话(语句的表达,数据的传递)
-
store:图书馆管理员(没办法记住所有书籍的存储情况)
-
reducers:图书馆管理员的记录本(要借什么书,先查有没有,要还的书查一下放到某个位置);
借书的人
——我要借一本书
——图书管理员听见
——查阅reducers手册
——去store找书
——把对应的书给借书人;
创建 store 和 reducers
// store
// ./store/index.js
import { createStore } from 'redux';
import reducer from './reducer'
const store = createStore(reducer);
export default store;
// ./store/reducer.js
const defaultState = { // 数据
inputValue: '123',
list: [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
]
}
// $$$ reducer 可以接收state 但是绝不能直接修改state $$$
export default (state = defaultState, action) => {
return state;
}
// index.js
import store from './store/index'
this.state = store.getState(); // 使用数据
Acition 和 Reducer 的编写
- 要想更新state中的数据,首先派发一个action,action通过dispatch方法传给store。
- store把之前的数据previousState和传过来的action自动转发给reducers函数。
- reducers接收state和action后进行数据处理,重新生成一个newState(原state只读不改),把newState作为返回值返回给store。
- store接收newState,将新数据替换原来的数据。
- react组件中观测到数据发生改变(store.subscribe),会从store里面重新取数据(state),更新组件的内容,页面发生变化。
ActionTypes 的拆分
// actiontypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
优点:好处是变量写错会报错 易于代码书写
action 的统一管理 actionCreator
// actionCreator.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes';
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
});
export const getAddItemAction = () => ({
type: ADD_TODO_ITEM
});
export const getDeleteItemAction = (index) => ({
type: DELETE_TODO_ITEM,
index
});
优点:把action都集中写在一个文件中,方便后期维护和自动化测试
Redux 总结
redux三个基本原则:
①:store必须是唯一的
②:只有store可以改变自己的内容
③:reducer 必须是纯函数
只有store能改变自己内容说明在reducer里我们不能直接操作state,只能通过定义新变量copy state的值,然后对新变量进行操作并 return 出新变量,不允许直接改变state。
什么是纯函数?
给固定的输入,就一定会有固定的输出,并且不会有任何副作用。
所以对于异步函数(定时器、ajax数据请求等)、动态时间都不适意在reducer里订阅。
store核心api:
- crerteStore(): 创建一个store
- store.dispatch(action): 派发action,传递store
- store.getState(): 获取store的所有数据
- store.subscribe(): 订阅store的变化,接收的回调函数在store改变时候就会自动执行
Redux 进阶
什么是 Redux 的中间件?
中间件:在action和store中间,对store的dispath方法的升级;
例如thunk中间件,使得dispath可以接受函数,如果传递过来的action是一个函数那么就先执行,如果传递过来的action是一个对象(action只能是一个对象,当使用了redux-thunk后,action可以是一个函数),那就直接传给reducers
- redux-thunk 把异步操作都放在action中
- redux-saga 把异步操作放在单独文件中
使用Redux-thunk 中间件实现ajax数据请求
- 不使用中间件,store接收的action只能是对象;有了中间件(redux-thunk),action可以是一个函数,通过store.dispatch这个方法将action函数传给store。
- store接收到action之后发现action是函数而不是对象,则会执行调用这个action函数。
- action函数内部先进行异步请求获取数据,之后去改变store中的数据(state)。
- 首先内部创建一个action对象,外部action这个函数默认接收store的dispatch方法,因此直接调用dispatch方法将内部action对象传给store,store便可以更新数据。
// index.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk),
);
const store = createStore(reducer, enhancer);
export default store;
// TodoList.js
componentDidMount() {
const action = getTodoList();
store.dispatch(action);
}
// actionTypes.js
export const INIT_LIST_ACTION = 'get_init_action';
// actionCreators.js
import { INIT_LIST_ACTION } from './actionTypes';
import axios from 'axios';
export const initListAction = (data) => ({
type: INIT_LIST_ACTION,
data
});
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json').then((res)=>{
const data = res.data;
const action = initListAction(data);
dispatch(action);
})
}
}
Redux-saga 中间件入门
// index.js
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
import mySaga from './sagas';
import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware)
);
const store = createStore(reducer, enhancer);
sagaMiddleware.run(mySaga);
export default store;
// sagas.js
import { takeEvery, put } from 'redux-saga/effects';
import { GET_INIT_LIST } from './actionTypes';
import { initListAction } from './actionCreators';
import axios from 'axios';
function* getInitList() {
const res = yield axios.get('/list.json');
const action = initListAction(res.data);
yield put(action);
}
// generator 函数
function* mySaga() {
yield takeEvery(GET_INIT_LIST, getInitList);
}
export default mySaga;
// actionTypes.js
export const GET_INIT_LIST = 'get_init_list';
// actionCreators.js
import { GET_INIT_LIST } from './actionTypes';
export const getInitList = () => ({
type: GET_INIT_LIST
});