react-redux进阶

Action
Action 是把数据从应用(服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。分下边两类

/* * action 常量 */ export const ADD_TODO = 'ADD_TODO'; export const VisibilityFilters = { SHOW_ALL: 'SHOW_ALL', SHOW_COMPLETED: 'SHOW_COMPLETED', }/* * action 创建函数 */ export function addTodo(text) { return { type: ADD_TODO, text } }export const addTodo = (id)=>{ return { type: EDITORUSERID, id:id } }

reducer
是一个纯函数,接收旧的 state 和 action,返回新的 state。
(previousState, action) => newState
注意:永远不要在 reducer 里做这些操作:
  • 修改传入参数;
  • 执行有副作用的操作,如 API 请求和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random()。
function todos(state = [], action) { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ] default: return state } }

combineReducers管理多个Reducer
const todoApp = combineReducers({ visibilityFilter, todos }) export default todoApp当你触发 action 后,combineReducers 返回的 todoApp 会负责调用两个 reducer: let nextTodos = todos(state.todos, action)注意:也可以 reducer 放到一个独立的文件中,通过 export 暴露出每个 reducer 函数import * as reducers from './reducers'

Store
Store 有以下职责:
  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器
import todoApp from './reducers' let store = createStore(todoApp)

createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
let store = createStore(todoApp, window.STATE_FROM_SERVER)
容器组件
Redux 的 React 绑定库是基于 容器组件和zhan shi组件相分离 的开发思想
展示组件 容器组件
作用 描述如何展现(骨架、样式) 描述如何运行(数据获取、状态更新)
直接使用 Redux
数据来源 props 监听 Redux state
数据修改 从 props 调用回调函数 向 Redux 派发 actions
调用方式 手动 通常由 React Redux 生成
展示组件就是一般的js文件容器组件往往使用connect(mapStateToProps,mapDispatchToProps) 创建。
mapStateToProps是把容器组件state向展示组件props映射。mapDispatchToProps() 是映射回调方法。例如,我们希望 VisibleTodoList 向 TodoList 组件中注入一个叫 mOnClick 的 props ,还希望 onTodoClick 能分发 increaseAction 这个 action:
const App=connect( (state)=>({ value:state.count }),(dispatch)=>({ mOnClick:()=>dispatch(increaseAction) }) )(Counter);

Provider
所有容器组件都可以访问 Redux store,建议的方式是使用指定的 React Redux 组件 来包裹,让所有容器组件都可以访问 store,
let store = createStore(todoApp)render(, document.getElementById('root') )

//创建组件的简单写法 const App = () => (
)export default App

高级部分(处理异步Action)
标准的做法是使用 Redux Thunk 中间件。
action 创建函数除了返回 action 对象外还可以返回函数。这时,这个 action 创建函数就成为了 thunk。这个函数会被 Redux Thunk middleware 执行。
我们仍可以在 actions.js 里定义这些特殊的 thunk action 创建函数。
创建thunk action
//thunk action // 虽然内部操作不同,你可以像其它 action 创建函数 一样使用它: // store.dispatch(fetchPosts('reactjs'))export function fetchPosts(subreddit) { return function (dispatch) {// 首次 dispatch:更新应用的 state 来通知 // API 请求发起了。 dispatch(requestPosts(subreddit))return fetch(`http://www.subreddit.com/r/${subreddit}.json`) .then( response => response.json(), error => console.log('An error occurred.', error) ) .then(json => dispatch(receivePosts(subreddit, json)) ) } }export function fetchPostsIfNeeded(subreddit) { // 当缓存的值是可用时, // 减少网络请求很有用。return (dispatch, getState) => { if (shouldFetchPosts(getState(), subreddit)) { // 在 thunk 里 dispatch 另一个 thunk! return dispatch(fetchPosts(subreddit)) } else { // 告诉调用代码不需要再等待。 return Promise.resolve() } } }

middleware 你可以利用 Redux middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。应用中间件要改造下createStore()
* 记录所有被发起的 action 以及产生的新的 state。 */ const logger = store => next => action => { console.group(action.type) let result = next(action) console.log('next state', store.getState()) return result }let store = createStore( todoApp, applyMiddleware( logger )

优化减少模版代码
1 action优化
1.1 你可以写一个用于生成 action creator 的函数:
function makeActionCreator(type, ...argNames) { return function(...args) { let action = { type } argNames.forEach((arg, index) => { action[argNames[index]] = args[index] }) return action } }const ADD_TODO = 'ADD_TODO' const EDIT_TODO = 'EDIT_TODO'export const addTodo = makeActionCreator(ADD_TODO, 'todo') export const editTodo = makeActionCreator(EDIT_TODO, 'id', 'todo')

1.2异步 Action Creators
export function loadPosts(userId) { return { // 要在之前和之后发送的 action types types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'], // 检查缓存 (可选): shouldCallAPI: (state) => !state.users[userId], // 进行取: callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`), // 在 actions 的开始和结束注入的参数 payload: { userId } }; }

2.reducer重构
方法抽取
function addTodo(state, action) { ... return updateObject(state, {todos : newTodos}); } function todoReducer(state = initialState, action) { switch(action.type) { case 'SET_VISIBILITY_FILTER' : return setVisibilityFilter(state, action); case 'ADD_TODO' : return addTodo(state, action); default : return state; } }

善用combineReducers函数
// 使用 ES6 的对象字面量简写方式定义对象结构 const rootReducer = combineReducers({ todoReducer, firstNamedReducer }); const store = createStore(rootReducer);

3.大多数应用会处理多种数据类型,通常可以分为以下三类:
  • 域数据(Domain data): 应用需要展示、使用或者修改的数据(比如 从服务器检索到的所有 todos
  • 应用状态(App state): 特定于应用某个行为的数据(比如 “Todo #5 是现在选择的状态”,或者 “正在进行一个获取 Todos 的请求”)
  • UI 状态(UI state): 控制 UI 如何展示的数据(比如 “编写 TODO 模型的弹窗现在是展开的”)
一个典型的应用 state 大致会长这样:
{ domainData1 : {}, domainData2 : {}, appState1 : {}, appState2 : {}, ui : { uiState1 : {}, uiState2 : {}, } }

必要时可采用
Redux-ORM
参考 github redux
Redux 中文文档
如有疏漏,请指出,如有问题可以通过如下方式联系我
【react-redux进阶】
csdn
掘金
klvens跑码场

    推荐阅读