gracekrcx / weekly-notes

4 stars 0 forks source link

[2020-03-18] Redux #80

Open gracekrcx opened 4 years ago

gracekrcx commented 4 years ago

A Predictable State Container for JS Apps

img img link

為什麼需要 redux

  1. 需要一個 global 存狀態的地方,狀態資料管理
  2. 在沒有 redux 時,跨 component 溝通會是麻煩的,我們只有 2 個地方可以獲得狀態,一個是 state 一個是 props。 state 是自己 component 內部的狀態,props 是藉由父層傳入的狀態 A state => B props => B props.callback => A state
  3. 使用 Redux 的一個益處就是讓 state 的變化過程『可預知』和『透明』,要改變 state 就是發一個 action
  4. Redux 架構圍繞著嚴格的『單向資料流』,讓 state 的變化更容易預測與了解。
  5. 把 view 和 data 改變的邏輯切開,有利維護

Three Principles

  1. Single source of truth 唯一真相來源 The state of your whole application is stored in an object tree within a single store. 整個 application 的 state 都存在樹狀物件放在唯一的 store 裡面,redux 只有唯一一個 store 將所有的資料用物件的方式包起來。

  2. State is read-only State 是唯讀的 The only way to change the state is to emit an action, an object describing what happened. 改變 state 的唯一的方式是發出一個 action,這個物件描述改變。

  3. Changes are made with pure functions 變更由 pure function 執行 To specify how the state tree is transformed by actions, you write pure reducers. 要指定 state tree 如何藉由 action 來轉變,你必須撰寫 pure reducer。

store

整個 Redux 就是以 store 為核心來運作 const store = createStore(reducers, preloadedState, enhancer); Returns (Store)

store 方法

1. store.getState() 返回當前的 state。 它與 store 的最後一個 reducer 返回值相同。

2. store.dispatch(action) 分發 action。這是觸發 state 變化的惟一途徑。

3. store.subscribe(render) 向 store 訂閱 dispatch 事件,當 dispatch 發生時會執行訂閱的 callback (render)

// create a store
const store = createStore(counter);
console.log(store.getState());  // 0

// dispatch an action
store.dispatch({ type: 'INCREMENT' });
console.log(store.getState());  // 1

// subscribe a callback function
store.subscribe(() => {
  document.body.innerText = store.getState();
});

connect 介紹

connect 是 'react-redux' 提供的 API The connect() function connects a React component to a Redux store.

Dan 的 connect.js 在 global state 和 component 之間再多一層,這一層確認有哪些 state 會進入這個 component,有哪些 dispatch 跟這個 component 有關係。

redux <=> connect <=> react

  1. mapStateToProps : 你需要哪些 state
const mapStateToProps = state => {
  return {
    // 這裡面的資料就會變成 props
    // 不需要的 state,就不用進入 component
    navText : state.nav.navText,  // state 直接用
    todos: getVisibleTodos(state.todos, state.visibilityFilter) // state 再經過一個 fn 處理 
  }
}
  1. mapDispatchToProps : dispatch 經由 connect 跟 redux 產生關聯

render() {
 return (
  <div onClick={()=>{
   this.props.updateNewNum(Math.random()) // component function name
  }}></div>
 )
}

const mapDispatchToProps = dispatch => {
 return {
  // component function name :  (參數) => dispatch( action )
  updateNewNum : num => dispatch(updateNum(num))
 }
}

action and action creator

Actions are like breadcrumbs of what has happened.

Action:一個 pure JavaScript object,描述發生的事件類別(type),以及所承載的資訊(payload)。 Action creator:幫助產生 Action 的 helper function

reducer

To tie state and actions together, we write a function called a reducer.

拿 old state 和 action 經過一些 logic 之後產生 new state 給 store reducer 裡面包含了 整理資料,處理資料的邏輯 action 資料進來後就看在 reducer 要怎麼改 reducer is pure function

reducer:產生新的 state,newState = reducer(prevState, action) (previousState, action) => newState

combineReducers

Note that each of these reducers is managing its own part of the global state. The state parameter is different for every reducer, and corresponds to the part of the state it manages.

import { combineReducers } from 'redux'

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp

Container 和 Components

  1. Container : 知道 redux,component 外面再加一層 container 去跟 redux 互動,如果有一天要把 redux 拔掉,也不會太困難,把架構切的更乾淨一點,所有的邏輯寫在 container,component 就單純接收 props 的 UI
  2. Components : 不知道 redux
  3. 在資料夾結構,可以再區分 Container 和 Components 出來 |- components 資料夾 |- container 資料夾

withRouter + redux

export default withRouter(connect(null, null)(HomeContainer))

寫 redux 的小方法

  1. 先寫『讀資料』的部分,再去寫 『修改』的部分,把流程再切開,更清楚(initial state 先設定,再串聯給 container 可以收到 props,再寫前面的 dispatch)

  2. 讀資料 2 steps,update 4 steps,所以先把簡單的寫好

react 非同步呼叫 api

  1. 如果 component 裡有呼叫 api 的 function,那會導致這個 component 不易測試
  2. 要放在 global state 的資料,更新的唯一方法就是 dispatch 一個 action
  3. api 邏輯和 component 邏輯(view)混在一起了
  4. 跟 component 有關係的是 date 不是 call api,最好的方法是 component 去 call 一個 function 然後取得 data,component 只管取 data 就好,是 call api,是取 cookie 還是其他的資料來源,都可以

(扣除 api) redux 與使用者互動流程

react-redux

  1. Redux 的 state 自動綁定到 react component 的 props
  2. 優化渲染機制
  3. <Provider /> 是 react-redux 中的組件 Provider Component 單純的把 store 用 context 傳下去,從最上層透過 context 傳整個 component tree,用 context 取代 props 來傳遞就可以在整個 component tree 中取用 context 而不用繁瑣的一層一層寫 props,不過要注意React.Children.only 的關係,Provider 只能有一個 Child Component <Provider />: Component providing the redux store 簡單說,react-redux 提供了 Provider,讓你把 store 注入到你的 react component 裡
  4. connect 是一個 Higher Order Function ,接受四個參數然後回傳一個 Higher Order Componentfunction
    const newComponent = connect(mapStatetoProps, ...)(Component)

img

參考文章

使用 Angular4 + Redux 做個 TodoMVC Three Principles 三大原則 Redux 基礎概念 Redux 中文文档 Redex 核心概念筆記 從 source code 來看 React-Redux 怎麼讓 Redux 跟 React 共舞

gracekrcx commented 4 years ago

https://redux.js.org/basics/usage-with-react#implementing-container-components 這個範例 code 在 todos 這個 state 進入 component 之前,getVisibleTodos 這個 function 也做了一些邏輯判斷 -> 當 component 裡面要用的資料,跟在 redux store 裡的資料結構不一樣的時候 ( component 需要的是“ 看得到的 todo” 但 redux store 存的是所有的 todos,所以要先篩選)

gracekrcx commented 4 years ago

Getting Started with Redux Dan 教你 redux

gracekrcx commented 4 years ago

Redux 原始碼的註解寫法

/**
 * Note that each middleware will be given the `dispatch` and `getState` functions
 * as named arguments.
 *
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
gracekrcx commented 4 years ago

我應該沒有在文章中誤導

gracekrcx commented 4 years ago

第一次看 O'Reilly 書裡說 redux 其實只有 99 行程式碼 其中有一個 utils/isPlainObject.ts

/**
 * @param obj The object to inspect.
 * @returns True if the argument appears to be a plain object.
 */
export default function isPlainObject(obj: any): boolean {
  if (typeof obj !== 'object' || obj === null) return false

  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }

  return Object.getPrototypeOf(obj) === proto
}

難道這就是優良的 code ???

  1. 不可能平空想出什麼事優良的 code 因為他好似從來不出現在我身上 所以是不是去看會寫出優良 code 的人 他們的 code 長怎樣,從模仿中學習
  2. google code review 是不是要讀一下
  3. 我想相關的書一定也不少