Redux入门
Redux入门
Redux自述
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 (如果你需要一个 WordPress 框架,请查看 Redux Framework。)
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。
Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
Redux对于JavaScript应用而言是一个可预测状态的容器。换言之,它是一个应用数据流框架,而不是传统的像underscore.js或者AngularJs那样的库或者框架。
在Redux中,所有的数据(比如state)被保存在一个称为store的容器中(在一个应用程序中只有一个)。store保存了所有的对象的状态,任何UI组件都可以直接从strore中获取指定对象的状态。如果要从本地或远程更改状态,需要分发一个action,action通过dispatch函数分发给store。store接收到action,将它给相关的reducer。reducer是一个纯函数,返回一个新的state。
为什么需要Redux?
React用state和props来进行组件的渲染,但是在工程庞大的项目里,这种做法不仅会使代码变得庞大而且不易于维护。Redux以 store为单一数据源,便于各个组件之间数据的传递。
Redux配置。
npm install redux --save //安装最新版本的Redux
npm install react-redux --save // React绑定库
npm install redux-devtools --save-dev // Redux开发者工具
Redux基本概念
- State
state是整个应用的数据,本质上是一个普通对象。
state决定了整个应用的组件如何渲染,渲染的结果是什么。
并不是所有的数据都保存在state当中,有些属于组件本身的数据完全可以留给组件本身去维护。
- Action
action就是把数据从应用传到store的有效载荷。它是state的唯一来源,一般可以通过store.dispatch(action)将action传到store。
action本质上是JS普通对象,action内必须有一个字符串type来表示要执行的动作。当规模越来越大的时候,可以令建一个文件actionTypes创建action中的type字符串。
示例,一个action:
{
type:'ADD_TODO',
text:'The world is beautiful.'
}
除了type字段外,action对象的结构完全由你自己决定。
有时,我们还需要添加一个action index来表示用户完成任务的动作***。
我们应该尽量少在action中传递数据。传递index就比把整个任务对象传过去要好。
action创建函数:
在大的项目中,action一个一个地手动创建不利于管理。一般会写好action的创建函数,即actionCreators.js。调用actionCreators会返回对应的action。
触发action:
创建好action后,如何触发这些action呢。使用reduxAPI中的dispatch函数,store.dispatch(action)
- Reducer
action只是描述了有事情发生这一事实,并没有描述应用如何更新state。reducer指定了应用状态的变化和如何响应action并发送到store的。
reducer是state最终格式的确定。它是一个纯函数,也就是说,只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,只是单纯执行计算。
reducer就是一个纯函数,接收旧的state和action,返回新的state。
比如,一个加减的reducer:
import {INCREMENT_COUNTER,DECREMENT_COUNTER} from '../actions';
export default function counter(state = 0, action) {
switch (action.type){
case INCREMENT_COUNTER:
return state+1;
case DECREMENT_COUNTER:
return state-1;
default:
return state;
}
}
对于大的项目,我们是需要将reducer给拆分的,最后通过redux提供的combineReducers方法组合在一起,就如上面的reducer
const rootReducer = combineReducers({
counter
});
export default rootReducer;
每个reducer只负责全局state中它负责的那一部分。每个reducer的state参数都不同,分别对应它管理的那部分state数据。
- store
store就是把action和reducer联系在一起的对象,具有如下职责:
- 维持应用的state
- 提供**getState()**方法获取 state
- 提供**dispatch(action)**方法更新 state
- 通过**subscribe(listener)**注册监听器
- 通过**subscribe(listener)**返回的函数注销监听器
Redux应用只有一个单一的store。当需要拆分数据处理逻辑时,应该使用reducer组合而不是建立多个store。
假设我们已经用combineReducers()将多个reducer合并为一个todoApp,先在我们将其导入,并传递createStore()。
import { createStore } from 'redux';
import todoApp from './reducers';
let store = createStore(todoApp);
store的创建通过redux的createStore方法创建,这个方法还需要传入reducer,很容易理解:毕竟我需要dispatch一个action来改变state嘛。 应用一般会有一个初始化的state,所以可选为第二个参数,这个参数通常是有服务端提供的,传说中的Universal渲染。 第三个参数一般是需要使用的中间件,通过applyMiddleware传入。
action,store,actionCreator,reducer之间的关系就是如下了:
搭配React
这里需要强调一下:Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。
尽管如此,Redux 还是和 React 和 Deku 这类库搭配起来用最好,因为这类库允许你以 state 函数的形式来描述界面,Redux 通过 action 的形式来发起 state 变化。
- 安装React Redux
Redux默认并不包含React绑定库,需要单独安装
npm install react-redux --save
- UI组件和容器组件
React-redux将组件分成了两大类:UI组件和容器组件。简单来说,UI组件用来描述如何展现,容器组件是描述如何运行(数据获取,状态更新)。
- connect()
容器组件需要connect函数,connect函数:mapStateToProps和mapDispatchToProps。
mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中。
mapDispatchToProps 方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法。
-传入store
所有容器组件都可以访问Redux store,所以可以手动监听它。
使用指定的React Redux组件****来让所有的组件都可以访问store。
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
React-Redux代码规范
- 创建应用store
在src目录下,创建store文件夹,在其下建立index.js(创建store)和reducer.js(整理reducer组合)
- reducer.js:
//被拆分的各个reducer
import { reducer as headerReducer} from '../common/Header/store';
import { reducer as homeReducer} from '../pages/home/store';
import { reducer as detailReducer } from '../pages/detail/store';
import { reducer as loginReducer } from '../pages/login/store';
const reducer =combineReducers ({
header:headerReducer,
home:homeReducer,
detail:detailReducer,
login:loginReducer
});
export default reducer;
- index.js:
import { createStore ,applyMiddleware } from 'redux';
import thunk from 'redux-thunk'; // 使用redux中间件redux-thunk
import reducer from './reducer';
const store = createStore(reducer,applyMiddleware(thunk));
export default store;
- 传入store
在React路由管理文件中修改,比如src下的App.js。
- App.js:
import React, { Component } from 'react';
import { BrowserRouter,Route } from 'react-router-dom';
import Header from './common/Header';
import Login from './pages/login';
import store from './store/index.js'; // 引入store库
import { Provider } from 'react-redux'; // 引入Provider
class App extends Component {
render() {
return (
<Provider store={store}> // 被<Provider>包含的组件都可以访问store中的数据
<BrowserRouter>
<div>
<Header />
<Route path='/login' exact component={Login} ></Route>
</div>
</BrowserRouter>
</Provider>
);
}
}
export default App;
- 被拆分的reducer
在需要使用reducer的组件文件夹下创建store文件夹,创建index.js、reducer.js、actionTypes.js、actionCreators.js四个文件。
例如Login组件:
- actionTypes.js:(用于存放各种action.type)
export const CHANGE_LOGIN = 'login/CHANGE_LOGIN';
export const CHANGE_LOGOUT = 'login/CHANGE_LOGOUT';
- actionCreators.js:(action的创建函数)
import * as actionTypes from './actionTypes';
export const logout = () => ({
type:actionTypes.CHANGE_LOGOUT,
value:false
})
- reducer.js:
import * as actionTypes from './actionTypes';
const defaultState = {
login:true
}
export default ( state = defaultState,action ) => {
if(action.type === actionTypes.CHANGE_LOGOUT){
return state.set('login',action.value) // 需要注意的是state不能直接改变,只能返回一个新的state
}
return state;
}
- index.js:
import reducer from './reducer'
import * as actionCreators from './actionCreators';
import * as actionTypes from './actionTypes';
export { reducer,actionCreators,actionTypes }; // 将reducer,actionTypes,actionCreators归总导出
- 使用store
就如上述例子的Login组件。在login文件夹下建index.js来写组件。
import { connect } from 'react-redux'; // 引入connect函数
import { actionCreators } from './store';