摘要:異步數據管理一直是前端的一個重點和難點,可以這么說,的應用會有異步數請求據并在中消費,并且在相當多的應用中,處理異步數據是它的核心業務邏輯。總結個人認為,異步數據不應該使用狀態管理來維護,應該放在組件內。
異步數據管理一直是前端的一個重點和難點,可以這么說,80%的 web 應用會有異步數請求據并在 UI 中消費,并且在相當多的 web 應用中,處理異步數據是它的核心業務邏輯。
在 React 的生態圈中,大部分人把異步數據使用狀態管理維護,比如使用 Redux,用異步 Action 獲取遠程數據,然后存在 store 中。
但在這個時間節點,9012 年了,我認為使用狀態管理去維護異步數據不是一種優雅的方式,React Hooks 出現后,我認為直接在組件內維護異步數據更加合理。不管從開發效率還是可維護性看,都比使用狀態管理好。
為什么這說呢?下面我們通過代碼來看看。
現在,假設我們要實現一個功能,獲取一個 TodoList 數據,并且用組件渲染。
最簡單是直接在組件內使用生命周期獲取數據,然后存在組件內部的 state 中。
使用 React 生命周期import React from "react" class Todos extends React.Component { constructor(props) { super(props) this.state = { loading: false, todos: [], error: null, } } async componentDidMount() { this.setState({ loading: true }) try { const todos = await (await fetch( "https://jsonplaceholder.typicode.com/todos", )).json() this.setState({ todos, loading: false }) } catch (error) { this.setState({ error, loading: false }) } } render() { const { loading, todos, error } = this.state if (loading) return loading... if (error) return error! return (
在線 Demo 請看:
這種方式非常非常符合人的直覺,但最大的問題是:外部無法改變異步數據,組件渲染后數據就無法再改變。這也是大部分人使用狀態管理維護異步數據的緣由。
下面我們看看如何使用 Redux 維護異步數據。
使用 Redux假設我們已經使用了 Redux 中間件 redux-thunk,我們會有下面類似的代碼:
首先,我們會把字符串定義定義為常量到一個 constant.js
export const LOADING_TODOS = "LOADING_TODOS" export const LOAD_TODOS_SUCCESS = "LOAD_TODOS_SUCCESS" export const LOAD_TODOS_ERROR = "LOAD_TODOS_ERROR"
然后,編寫異步的 action, actions.js:
import { LOADING_TODOS, LOAD_TODOS_SUCCESS, LOAD_TODOS_ERROR, } from "../constant" export function fetchTodos() { return dispatch => { dispatch({ type: LOADING_TODOS }) return fetch("https://jsonplaceholder.typicode.com/todo") .then(response => response.json()) .then(todos => { dispatch({ type: LOAD_TODOS_SUCCESS, todos, }) }) .catch(error => { dispatch({ type: LOAD_TODOS_ERROR, error, }) }) } }
接著,在 reducer 中處理數據,todos.js
import { LOADING_TODOS, LOAD_TODOS_SUCCESS, LOAD_TODOS_ERROR, } from "../constant" const initialState = { loading: false, data: [], error: null, } export default function(state = initialState, action) { switch (action.type) { case LOADING_TODOS: return { ...state, loading: true, } case LOAD_TODOS_SUCCESS: return { ...state, data: action.todos, loading: false, } case LOAD_TODOS_ERROR: return { ...state, error: action.error, loading: false, } default: return state } }
還沒完,最后,在組件中使用:
import React, { Component } from "react" import { connect } from "react-redux" import { fetchTodos } from "../actions" class Todos extends Component { componentDidMount() { const { dispatch } = this.props dispatch(fetchTodos) } render() { const { loading, items, error } = this.props if (loading) return loading... if (error) return error! return (
在線 Demo 請看:
我們可以發現,使用 Redux 管理異步數據,代碼量激增,啰嗦冗余,模板代碼一堆,,不管開發效率還是開發體驗,亦或是可以維護性和可讀性,個人認為,類似的 redux 這樣的解決方案并不優雅。
下面我們看看如何使用 React Hooks 獲取異步數據。
使用 React Hooks我們使用 一個庫叫dahlia-rest 的 useFetch 獲取數據,可以輕松的拿到數據的狀態 { loading, data, error },然后渲染處理:
import React from "react" import { useFetch } from "dahlia-rest" const Todos = () => { const { loading, data, error } = useFetch( "https://jsonplaceholder.typicode.com/todos", ) if (loading) return loading... if (error) return error! return (
dahlia-rest的完整用法可以看 dahlia-rest
在線 Demo 請看:
代碼非常簡潔,loading 狀態和錯誤處理非常優雅,也許你發現了,貌似這也和使用生命周期一樣,外部無法改變數據狀態,其實不是的,下面重會點講講如何更新數據。
Hooks 更新異步數據使用 hooks 維護異步數據,有三種方式更新異步數據,這里用 dahlia-rest 舉例。
內部 refetch這是最簡單的重新獲取數據的方式,通常,如果觸發更新的動作和useFetch在統一組件內,可以使用這種方式。
const Todos = () => { const { loading, data, error, refetch } = useFetch("/todos", { query: { _start: 0, _limit: 5 }, // first page }) if (loading) return loading... if (error) return error! const getSecondPage = () => { refetch({ query: { _start: 5, _limit: 5 }, // second page }) } return () }{data.map(item => (
- {item.title}
))}
在線 Demo 請看:
通過更新依賴來重新獲取數據,這也是常用的方式之一,因為在很多業務場景中,觸發動作會在其他組件中,下面演示如何通過更新依賴觸發數據更新:
這里使用一個簡單的狀態管理庫維護依賴對象,狀態管理的完整文檔請看dahlia-store。
定義一個 store 用來存放依賴:
// /stores/todoStore.ts import { createStore } from "dahlia-store" const todoStore = createStore({ params: { _start: 0, _limit: 5, }, updateParams(params) { todoStore.params = params }, })
在組件中,使用依賴:
import { observe } from "dahlia-store" import todoStore from "@stores/todoStore" const Todos = observe(() => { const { params } = todoStore const { loading, data, error } = useFetch("/todos", { query: params, deps: [params], }) if (loading) return loading... if (error) return error! const updatePage = () => { todoStore.updateParams({ _start: 5, _limit: 5 }) } return () }){data.map(item => (
- {item.title}
))}
在線 Demo 請看:
你可以在任意地方,不管組件內還是組件外,你都可以可以調用todoStore.updateParams更新依賴,從而實現數據更新。
注意:這里的依賴是個對象,你必須更新整個對象的引用,如果你只更新對象的屬性是無效的。
使用 fetcher有時候,你需要在組件外部重新獲取數據,但useFetch 卻沒有任何可以被依賴的參數,這時你可以使用 fetcher
import { useFetch, fetcher } from "dahlia/rest" const Todos = () => { const { loading, data, error } = useFetch("/todos", { name: "GetTodos" }) if (loading) return loading... if (error) return error! return (
在線 Demo 請看:
使用 fetcher 是,你需要為useFetch 提供 name 參數,用法是:fetcher["name"].refetch(),這里的 refetch 和內部 refetch 是同一個函數,所以它也有 options 參數。
總結個人認為,異步數據不應該使用狀態管理來維護,應該放在組件內。對于大多數 web 應用,狀態管理中的數據應該是比較薄的一層,并且應該避免在狀態管理中處理異步帶來的副作用。也許,Redux 默認不支持處理異步數據,是一個相當有遠見的決定。
我們發現,使用 Hooks 管理異步數據,代碼非常簡潔,有一種大道至簡感覺和返璞歸真感覺。幾行代碼就能寫完功能,為什么要搞出那么長的鏈路,搞那么繞的邏輯。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104111.html
摘要:在項目開始之前,不能心急立刻去搭建,需要設定幾個步驟來開展,接下來大概的說一下我從技術選型到項目前端搭建好的整個生命周期。開發該項目的底層的內容遠不止這些,但由于公司制度規定,只能大概的闡述了在從接手到選型到搭建完畢這到的過程的做法和思考。 前段時間部門要基于一個系統的基礎上開發一個管理平臺,于是我接手了該平臺的重活,因為上一個平臺我用了vue搭建,所以這次想用react來搭建。在項目...
摘要:是分發器,是數據與邏輯處理器,會在注冊針對各個命令字的響應回調函數。當按如下方式觸發回調時,回調函數具備事件的特性。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇解釋 Shadow Widget 在 MVC、MVVM、Flux 框架之間如何做選擇。 showImg(https://segmentfault.com/img/bVOODj?w=380&h...
摘要:前端日報精選在中的元素種類及性能優化譯異步遞歸回調譯定位一個頁面阻塞問題的排查過程前端分享之的使用及單點登錄中文視頻如何用做好一個大型應用云際個實用技巧眾成翻譯年一定不要錯過的五本編程書籍年前端領域有哪些探索和實踐實現一個時光網掘金 2017-09-22 前端日報 精選 JavaScript 在 V8 中的元素種類及性能優化【譯】異步遞歸:回調、Promise、Async[譯]HTML...
閱讀 3421·2021-10-20 13:49
閱讀 2793·2021-09-29 09:34
閱讀 3691·2021-09-01 11:29
閱讀 3081·2019-08-30 11:01
閱讀 838·2019-08-29 17:10
閱讀 867·2019-08-29 12:48
閱讀 2777·2019-08-29 12:40
閱讀 1348·2019-08-29 12:30