iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
1

Thunk

經過上一篇了解了 Redux 和如何實做 Middleware 之後

再來看看 Thunk 到底做了什麼事情呢?

What is Thunk?

Thunk 是一個 Function 主要功用是將結果延遲到需要的時候再執行這個 Function 來獲取這份資料

function foo() {
  return 1 + 2;
}

const x = 1 + 2;

這是最簡單的一個 Thunk

這時候來看一下 React Thunk 到底在 applyMiddleware 做了什麼事情?

React Thunk

它其實是多包了一層 Function 來暫時阻止 action 進入 Store

來達到有機會完成非同步的 Action

import { createStore, applyMiddleware } from 'redux';
import React, {useState, useEffect} from 'react';
import thunk from 'redux-thunk';
import { View, Text } from 'react-native';

const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};


const store = createStore(counter,  applyMiddleware(thunk));

function increment() {
  console.log('increment');
  return {
    type: 'INCREMENT',
  };
}

function incrementAsync() {
  console.log('incrementAsync');
  return (dispatch) => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 1000);
  };
}

const LoginScreen = () => {
  const [value, setValue] = useState(null);
  useEffect(() => {
    incrementAsync()(store.dispatch);

    const unsubscribe = store.subscribe(() => {
      const v = store.getState();
      console.log('LoginScreen -> v', v)
      setValue(v);
    })
    return () => unsubscribe();
  }, [])
  return (
    <View style={{flex: 1}}>
      <Text>{String(value)}</Text>
     
    </View>
  );
};

thunk 的 middleware 會多包覆一層 傳送 dipspatch

所以在執行 Action 的當下 Reducer 不會收到通知

而是在在中間曾發送 Action

來達到通知 Reducer 修改 Store 的效果

另外也可以做一些變形

讓這一層可以統一執行

範例

在 middleware 中有一個 promiseMiddleware.js 專門處理非同步事件

讓 Action 可以很單純的回傳一個物件

覺的是一種不錯的作法

Saga

Saga Pattern

Saga Pattern 並不是 Redux 產生的

各個程式語言都可以去實做這個 Pattern

這個 Pattern 想解決的是 LLT (Long Live Transaction) 的問題

尚未解決的是當產生了一個 Transaction 的同時也應該會產出一個 Compensation

在 Redux Saga 使用的2是 Generator Function 而不是 Async/ Await Function

所以要先了解這兩個的差異性

Async/Await And Gernerator

Redux Saga

如何透過 Generator 管理任務

一個一般的 function 依序列出 step1 step2 step3

function work() {
  console.log('step1');
  console.log('step2');
  console.log('step3');
}

work(); 

但是如果是要用 Generator Function 來實做的話

function* generatorWork() {
  yield console.log('step1');
  yield console.log('step2');
  yield console.log('step3');
}

const work = generatorWork();
work.next(); //step1
work.next();//step2
work.next();//step3

當每次呼叫 next 會回傳一個 {done: false, value: 'step1'} 的物件

done 是一個 Boolean

代表是否完成

Generator Runner

import axios from "axios";

function runner(genFn) {
  const itr = genFn();

  function next(arg) {
    let result = itr.next(arg);

    if (result.done) {
      return arg;
    } else {
      return Promise.resolve(result.value).then(next);
    }
  }

  return next();
}

function* genFn() {
  const USER_URI = "https://reqres.in/api/users";
  let res = yield axios.get(USER_URI);
  const userId = res.data.data[0].id;
  yield axios.get(`${USER_URI}/${userId}`);
}

const result = runner(genFn);
Promise.resolve(result).then(res => console.log(res.data));

Redux-Saga 是這樣來管理每一個任務

它可以執行的對象相當豐富

  • effect
  • iterator
  • promise
  • 一般的程式碼

Producer And Consumer

Redux-saga 其實背後的原理是 Producer 和 Consumer

image

但是不知道是歷史因素還是什麼原因

image

如果你閱讀 redux-saga 的原始碼

你會看到 channel 是用來管理非同步任務的緩衝區 (Buffer)

裡面提供了 produce 與 consume 的函式

  • channel.take()

    • 生產者 (Producer): 把任務放到 channel 中
  • channel.put()

    • 消費者 (Consumer): 呼叫了 store.dispatch() 後執行的函式,會從 channel 中選擇符合 pattern 的任務執行

    WTF

take建立一個 Effect 描述

指示 middleware 在 Store 等待指定的 action

Generator 會暫停

直到一個符合 pattern 的 action 被 dispatch

簡單來說 take 是用來註冊處理非同步的函式

take 會 將處理非同步的函式所生成的 iterator 用 generator runner 包裝起來

最後呼叫 channel.take(cb)

以 callback 的形式儲存在 channel 中

在 redux-saga 的實作中

被儲存在 channel 中的 callback 稱作 taker

這些原理講得有點抽象

下個章節來看一下 Redux-saga 的 Source code 吧


上一篇
Redux Part I
下一篇
Redux-saga-part III
系列文
I Shot You 不小心 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言