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
数据修改从 props 调用回调函数
调用方式手动

展示组件就是一般的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(
  <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跑码场

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值