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 组件 <Provider> 来包裹,让所有容器组件都可以访问 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跑码场

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容