react-redux 进阶

Action

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

/*
 * 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) => newStatereact

注意:永远不要在 reducer 里作这些操做:git

  • 修改传入参数;
  • 执行有反作用的操做,如 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管理多个Reducergithub

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 有如下职责:json

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 经过 subscribe(listener) 注册监听器;
  • 经过 subscribe(listener) 返回的函数注销监听器
import todoApp from './reducers'
let store = createStore(todoApp)
复制代码

createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时很是有用,服务器端 redux 应用的 state 结构能够与客户端保持一致, 那么客户端能够将从网络接收到的服务端 state 直接用于本地数据初始化。redux

let store = createStore(todoApp, window.STATE_FROM_SERVER)api

容器组件

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:bash

const App=connect(
    (state)=>({
        value:state.count
    }),(dispatch)=>({
        mOnClick:()=>dispatch(increaseAction)
    })
)(Counter);
复制代码

Provider

全部容器组件均可以访问 Redux store,建议的方式是使用指定的 React Redux 组件 来包裹,让全部容器组件均可以访问 store,服务器

let store = createStore(todoApp)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

复制代码
//建立组件的简单写法
const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

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 中文文档

若有疏漏,请指出,若有问题能够经过以下方式联系我

简书 csdn 掘金 klvens跑码场

相关文章
相关标签/搜索