国产gaysexchina男同gay,japanrcep老熟妇乱子伦视频,吃奶呻吟打开双腿做受动态图,成人色网站,国产av一区二区三区最新精品

Redux 減少樣板代碼

2021-09-16 10:07 更新

減少樣板代碼

Redux 很大部分 受到 Flux 的啟發(fā),并且最常見(jiàn)的關(guān)于 Flux 抱怨是它如何使得你寫了一大堆的模板。在這個(gè)技巧中,我們將考慮 Redux 如何使得我們選擇我們的代碼會(huì)變得怎樣繁復(fù),取決于個(gè)人樣式,團(tuán)隊(duì)選項(xiàng),長(zhǎng)期可維護(hù)等等。

Actions

Actions 是描述了在 app 中所發(fā)生的,以單獨(dú)方式描述對(duì)象變異意圖的服務(wù)的一個(gè)普通對(duì)象。很重要的一點(diǎn)是 你必須分發(fā)的 action 對(duì)象并不是一個(gè)模板,而是 Redux 的一個(gè)基本設(shè)計(jì)選項(xiàng).

有些框架生成自己和 Flux 很像,不過(guò)缺少了 action 對(duì)象的概念。為了變得可預(yù)測(cè),這是一個(gè)從 Flux or Redux 的倒退。如果沒(méi)有可串行的普通對(duì)象 action,便無(wú)法記錄或重放用戶會(huì)話,或者無(wú)法實(shí)現(xiàn) 帶有時(shí)間旅行的熱重載。如果你更喜歡直接修改數(shù)據(jù),那么你并不需要 Redux 。

Action 一般長(zhǎng)這樣:

{ type: 'ADD_TODO', text: 'Use Redux' }
{ type: 'REMOVE_TODO', id: 42 }
{ type: 'LOAD_ARTICLE', response: { ... } }

一個(gè)約定俗成的是 actions 擁有一個(gè)定值 type 幫助 reducer (或 Flux 中的 Stores ) 識(shí)別它們。我們建議的你使用 string 而不是 Symbols 作為 action type ,因?yàn)?string 是可串行的,而使用 Symbols 的話你會(huì)把記錄和重演變得比所需要的更難。

在 Flux 中,傳統(tǒng)上認(rèn)為你將每個(gè) action type 定義為string定值:

const ADD_TODO = 'ADD_TODO';
const REMOVE_TODO = 'REMOVE_TODO';
const LOAD_ARTICLE = 'LOAD_ARTICLE';

這么做的優(yōu)勢(shì)?人們通常聲稱定值不是必要的,對(duì)于小的項(xiàng)目可能是正確的。 對(duì)于大的項(xiàng)目,將action types定義為定值有如下好處:

  • 幫助維護(hù)命名一致性,因?yàn)樗械?action type 匯總在同一位置。
  • 有的時(shí)候,在開(kāi)發(fā)一個(gè)新功能之前你想看到所有現(xiàn)存的 actions ??赡艿那闆r是你的團(tuán)隊(duì)里已經(jīng)有人添加了你所需要的action,而你并不知道。
  • Action types 列表在Pull Request中能查到所有添加,刪除,修改的記錄。這能幫助團(tuán)隊(duì)中的所有人及時(shí)追蹤新功能的范圍與實(shí)現(xiàn)。
  • 如果你在導(dǎo)入一個(gè) Action 定值的時(shí)候拼寫錯(cuò)誤,你會(huì)得到 undefined 。當(dāng)你納悶 action 被分發(fā)出去而什么也沒(méi)發(fā)生的時(shí)候,一個(gè)拼寫錯(cuò)誤更容易被發(fā)現(xiàn)。

你的項(xiàng)目的約定取決與你自己。你開(kāi)始的時(shí)候可能用的是inline string,之后轉(zhuǎn)為定值,也許之后將他們歸為一個(gè)獨(dú)立文件。Redux 不會(huì)給予任何建議,選擇你自己最喜歡的。

Action Creators

另一個(gè)約定是,你創(chuàng)建生成 action 對(duì)象的函數(shù),而不是在你分發(fā)的時(shí)候內(nèi)聯(lián)生成它們。

例如,用文字對(duì)象取代調(diào)用 dispatch

// somewhere in an event handler
dispatch({
  type: 'ADD_TODO',
  text: 'Use Redux'
});

你可以在單獨(dú)的文件中寫一個(gè) action creator ,然后從 component 里導(dǎo)入:

actionCreators.js

export function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

AddTodo.js

import { addTodo } from './actionCreators';

// event handler 里的某處
dispatch(addTodo('Use Redux'))

Action creators 總被當(dāng)作模板受到批評(píng)。好吧,其實(shí)你并不用把他們寫出來(lái)!如果你覺(jué)得更適合你的項(xiàng)目你可以選用對(duì)象文字 然而,你應(yīng)該知道寫 action creators 是存在某種優(yōu)勢(shì)的。

假設(shè)有個(gè)設(shè)計(jì)師看完我們的原型之后回來(lái)說(shuō),我們需要允許三個(gè) todo 不能再多了。我們可以使用 redux-thunk 中間件添加一個(gè)提前退出,把我們的 action creator 重寫成回調(diào)形式:

function addTodoWithoutCheck(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

export function addTodo(text) {
  // Redux Thunk 中間件允許這種形式
  // 在下面的 “異步 Action Creators” 段落中有寫
  return function (dispatch, getState) {
    if (getState().todos.length === 3) {
      // 提前退出
      return;
    }

    dispatch(addTodoWithoutCheck(text));
  }
}

我們剛修改了 addTodo action creator 的行為,對(duì)調(diào)用它的代碼完全不可見(jiàn)。我們不用擔(dān)心去看每個(gè)添加 todo 的地方保證他們有了這個(gè)檢查 Action creator 讓你可以解耦額外的分發(fā) action 邏輯與實(shí)際的 components 發(fā)送這些 actions,而且當(dāng)你在重開(kāi)發(fā)經(jīng)常要改變需求的時(shí)候也會(huì)非常有用。

生成 Action Creators

某些框架如 Flummox 自動(dòng)從 action creator 函數(shù)定義生成 action type 定值。這個(gè)想法是說(shuō)你不需要 ADD_TODO 定值和 addTodo() action creator兩個(gè)都自己定義。這樣的方法在底層也生成 action type 定值,但他們是隱式生成的,也就是間接級(jí)。

我們不建議用這樣的方法。如果你寫像這樣簡(jiǎn)單的 action creator 寫煩了:

export function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

export function removeTodo(id) {
  return {
    type: 'REMOVE_TODO',
    id
  };
}

你可以寫一個(gè)生成 action creator 的函數(shù):

function makeActionCreator(type, ...argNames) {
  return function(...args) {
    let action = { type };
    argNames.forEach((arg, index) => {
      action[argNames[index]] = args[index];
    });
    return action;
  }
}

export const addTodo = makeActionCreator('ADD_TODO', 'todo');
export const removeTodo = makeActionCreator('REMOVE_TODO', 'id');

參見(jiàn) redux-action-utilsredux-actions 獲得更多介紹這樣的常用工具。

注意這樣的工具給你的代碼添加了魔法。魔法和間接聲明真的值得多寫一兩行代碼么?

異步 Action Creators

中間件 讓你注入一個(gè)定制邏輯,可以在每個(gè) action 對(duì)象分發(fā)出去之前解釋。異步 actions 是中間件的最常見(jiàn)用例。

沒(méi)有中間件的話,dispatch 只能接收一個(gè)普通對(duì)象。所以我們?cè)?components 里面進(jìn)行 AJAX 調(diào)用:

actionCreators.js

export function loadPostsSuccess(userId, response) {
  return {
    type: 'LOAD_POSTS_SUCCESS',
    userId,
    response
  };
}

export function loadPostsFailure(userId, error) {
  return {
    type: 'LOAD_POSTS_FAILURE',
    userId,
    error
  };
}

export function loadPostsRequest(userId) {
  return {
    type: 'LOAD_POSTS_REQUEST',
    userId
  };
}

UserInfo.js

import { Component } from 'react';
import { connect } from 'react-redux';
import { loadPostsRequest, loadPostsSuccess, loadPostsFailure } from './actionCreators';

class Posts extends Component {
  loadData(userId) {
    // 調(diào)用 React Redux `connect()` 注入 props :
    let { dispatch, posts } = this.props;

    if (posts[userId]) {
      // 這里是被緩存的數(shù)據(jù)!啥也不做。
      return;
    }

    // Reducer 可以通過(guò)設(shè)置 `isFetching` 反應(yīng)這個(gè) action 
    // 因此讓我們顯示一個(gè) Spinner 控件。
    dispatch(loadPostsRequest(userId));

    // Reducer 可以通過(guò)填寫 `users` 反應(yīng)這些 actions
    fetch(`http://myapi.com/users/${userId}/posts`).then(
      response => dispatch(loadPostsSuccess(userId, response)),
      error => dispatch(loadPostsFailure(userId, error))
    );
  }

  componentDidMount() {
    this.loadData(this.props.userId);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.userId !== this.props.userId) {
      this.loadData(nextProps.userId);
    }
  }

  render() {
    if (this.props.isLoading) {
      return <p>Loading...</p>;
    }

    let posts = this.props.posts.map(post =>
      <Post post={post} key={post.id} />
    );

    return <div>{posts}</div>;
  }
}

export default connect(state => ({
  posts: state.posts
}))(Posts);

然而,不久就需要再來(lái)一遍,因?yàn)椴煌?components 從同樣的 API 端點(diǎn)請(qǐng)求數(shù)據(jù)。而且,我們想要在多個(gè)components 中重用一些邏輯(比如,當(dāng)緩存數(shù)據(jù)有效的時(shí)候提前退出)。

中間件讓我們寫的更清楚M的潛在的異步 action creators. 它使得我們分發(fā)普通對(duì)象之外的東西,并且解釋它們的值。比如,中間件能 “捕捉” 到已經(jīng)分發(fā)的 Promises 并把他們變?yōu)橐粚?duì)請(qǐng)求和成功/失敗 actions.

最簡(jiǎn)單的中間件例子是 redux-thunk. “Thunk” 中間件讓你把 action creators 寫成 “thunks”,也就是返回函數(shù)的函數(shù)。 這使得控制被反轉(zhuǎn)了: 你會(huì)像一個(gè)參數(shù)一樣取得 dispatch ,所以你也能寫一個(gè)多次分發(fā)的 action creator 。

注意

Thunk 只是中間件的一個(gè)例子。中間件不是關(guān)于 “讓你分發(fā)函數(shù)” 的:它是關(guān)于讓你分發(fā)你用的特定中間件知道如何處理的任何東西的。Thunk 中間件添加了一個(gè)特定的行為用來(lái)分發(fā)函數(shù),但這實(shí)際上取決于你用的中間件。

考慮上面的代碼用 redux-thunk 重寫:

actionCreators.js

export function loadPosts(userId) {
  // 用 thunk 中間件解釋:
  return function (dispatch, getState) {
    let { posts } = getState();
    if (posts[userId]) {
      // 這里是數(shù)據(jù)緩存!啥也不做。
      return;
    }

    dispatch({
      type: 'LOAD_POSTS_REQUEST',
      userId
    });

    // 異步分發(fā)原味 actions 
    fetch(`http://myapi.com/users/${userId}/posts`).then(
      response => dispatch({
        type: 'LOAD_POSTS_SUCCESS',
        userId,
        respone
      }),
      error => dispatch({
        type: 'LOAD_POSTS_FAILURE',
        userId,
        error
      })
    );
  }
}

UserInfo.js

import { Component } from 'react';
import { connect } from 'react-redux';
import { loadPosts } from './actionCreators';

class Posts extends Component {
  componentDidMount() {
    this.props.dispatch(loadPosts(this.props.userId));
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.userId !== this.props.userId) {
      this.props.dispatch(loadPosts(nextProps.userId));
    }
  }

  render() {
    if (this.props.isLoading) {
      return <p>Loading...</p>;
    }

    let posts = this.props.posts.map(post =>
      <Post post={post} key={post.id} />
    );

    return <div>{posts}</div>;
  }
}

export default connect(state => ({
  posts: state.posts
}))(Posts);

這樣打得字少多了!如果你喜歡,你還是可以保留 “原味” action creators 比如從一個(gè) “聰明的” loadPosts action creator 里用到的 loadPostsSuccess 。

最后,你可以重寫中間件 你可以把上面的模式泛化,然后代之以這樣的異步 action creators :

export function loadPosts(userId) {
  return {
    // 要在之前和之后發(fā)送的 action types 
    types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],
    // 檢查緩存 (可選):
    shouldCallAPI: (state) => !state.users[userId],
    // 進(jìn)行?。?    callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),
    // 在 actions 的開(kāi)始和結(jié)束注入的參數(shù)
    payload: { userId }
  };
}

解釋這個(gè) actions 的中間件可以像這樣:

function callAPIMiddleware({ dispatch, getState }) {
  return function (next) {
    return function (action) {
      const {
        types,
        callAPI,
        shouldCallAPI = () => true,
        payload = {}
      } = action;

      if (!types) {
        // 普通 action:傳走
        return next(action);
      }

      if (
        !Array.isArray(types) ||
        types.length !== 3 ||
        !types.every(type => typeof type === 'string')
      ) {
        throw new Error('Expected an array of three string types.');
      }

      if (typeof callAPI !== 'function') {
        throw new Error('Expected fetch to be a function.');
      }

      if (!shouldCallAPI(getState())) {
        return;
      }

      const [requestType, successType, failureType] = types;

      dispatch(Object.assign({}, payload, {
        type: requestType
      }));

      return callAPI().then(
        response => dispatch(Object.assign({}, payload, {
          response: response,
          type: successType
        })),
        error => dispatch(Object.assign({}, payload, {
          error: error,
          type: failureType
        }))
      );
    };
  };
}

在傳給 applyMiddleware(...middlewares) 一次以后,你能用相同方式寫你的 API-調(diào)用 action creators :

export function loadPosts(userId) {
  return {
    types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],
    shouldCallAPI: (state) => !state.users[userId],
    callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),
    payload: { userId }
  };
}

export function loadComments(postId) {
  return {
    types: ['LOAD_COMMENTS_REQUEST', 'LOAD_COMMENTS_SUCCESS', 'LOAD_COMMENTS_FAILURE'],
    shouldCallAPI: (state) => !state.posts[postId],
    callAPI: () => fetch(`http://myapi.com/posts/${postId}/comments`),
    payload: { postId }
  };
}

export function addComment(postId, message) {
  return {
    types: ['ADD_COMMENT_REQUEST', 'ADD_COMMENT_SUCCESS', 'ADD_COMMENT_FAILURE'],
    callAPI: () => fetch(`http://myapi.com/posts/${postId}/comments`, {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ message })
    }),
    payload: { postId, message }
  };
}

Reducers

Redux 用函數(shù)描述邏輯更新減少了模版里大量的 Flux stores 。函數(shù)比對(duì)象簡(jiǎn)單,比類更簡(jiǎn)單得多。

考慮這個(gè) Flux store:

let _todos = [];

export default const TodoStore = assign({}, EventEmitter.prototype, {
  getAll() {
    return _todos;
  }
});

AppDispatcher.register(function (action) {
  switch (action.type) {
  case ActionTypes.ADD_TODO:
    let text = action.text.trim();
    _todos.push(text);
    TodoStore.emitChange();
  }
});

用了 Redux 之后,同樣的邏輯更新可以被寫成 reducing function:

export function todos(state = [], action) {
  switch (action.type) {
  case ActionTypes.ADD_TODO:
    let text = action.text.trim();
    return [...state, text];
  default:
    return state;
  }
}

switch 語(yǔ)句 不是 真正的模版。真正的 Flux 模版是概念性的:發(fā)送更新的需求,用 Dispatcher 注冊(cè) Store 的需求,Store 是對(duì)象的需求 (當(dāng)你想要一個(gè)哪都能跑的 App 的時(shí)候復(fù)雜度會(huì)提升)。

不幸的是很多人仍然靠文檔里用沒(méi)用 switch 來(lái)選擇 Flux 框架。如果你不愛(ài)用 switch 你可以用一個(gè)單獨(dú)的函數(shù)來(lái)解決,下面會(huì)演示。

生成 Reducers

讓我們寫一個(gè)函數(shù)使得我們將 reducers 表達(dá)為 action types 到 handlers 的映射對(duì)象。例如,在我們的 todos reducer 里這樣定義:

export const todos = createReducer([], {
  [ActionTypes.ADD_TODO](state, action) {
    let text = action.text.trim();
    return [...state, text];
  }
}

我們可以寫下面的幫忙函數(shù)來(lái)完成:

function createReducer(initialState, handlers) {
  return function reducer(state = initialState, action) {
    if (handlers.hasOwnProperty(action.type)) {
      return handlers[action.type](state, action);
    } else {
      return state;
    }
  }
}

不難對(duì)吧?Redux 沒(méi)有默認(rèn)提供這樣的幫忙函數(shù),因?yàn)橛泻枚喾N寫的方法??赡苣阆胍詣?dòng)把普通 JS 對(duì)象變成不可變對(duì)象通過(guò)濕化服務(wù)器狀態(tài)??赡苣阆牒喜⒎祷貭顟B(tài)和當(dāng)前狀態(tài)。有很多方法 “獲取所有” handler。這些都取決于你為你的團(tuán)隊(duì)在特定項(xiàng)目中選擇的約定。

Redux reducer 的 API 是 (state, action) => state,但是怎么創(chuàng)建這些 reducers 由你來(lái)定。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)