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跑碼場

相關文章
相關標籤/搜索