摘要:異步數(shù)據(jù)管理一直是前端的一個(gè)重點(diǎn)和難點(diǎn),可以這么說,的應(yīng)用會(huì)有異步數(shù)請求據(jù)并在中消費(fèi),并且在相當(dāng)多的應(yīng)用中,處理異步數(shù)據(jù)是它的核心業(yè)務(wù)邏輯??偨Y(jié)個(gè)人認(rèn)為,異步數(shù)據(jù)不應(yīng)該使用狀態(tài)管理來維護(hù),應(yīng)該放在組件內(nèi)。
異步數(shù)據(jù)管理一直是前端的一個(gè)重點(diǎn)和難點(diǎn),可以這么說,80%的 web 應(yīng)用會(huì)有異步數(shù)請求據(jù)并在 UI 中消費(fèi),并且在相當(dāng)多的 web 應(yīng)用中,處理異步數(shù)據(jù)是它的核心業(yè)務(wù)邏輯。
在 React 的生態(tài)圈中,大部分人把異步數(shù)據(jù)使用狀態(tài)管理維護(hù),比如使用 Redux,用異步 Action 獲取遠(yuǎn)程數(shù)據(jù),然后存在 store 中。
但在這個(gè)時(shí)間節(jié)點(diǎn),9012 年了,我認(rèn)為使用狀態(tài)管理去維護(hù)異步數(shù)據(jù)不是一種優(yōu)雅的方式,React Hooks 出現(xiàn)后,我認(rèn)為直接在組件內(nèi)維護(hù)異步數(shù)據(jù)更加合理。不管從開發(fā)效率還是可維護(hù)性看,都比使用狀態(tài)管理好。
為什么這說呢?下面我們通過代碼來看看。
現(xiàn)在,假設(shè)我們要實(shí)現(xiàn)一個(gè)功能,獲取一個(gè) TodoList 數(shù)據(jù),并且用組件渲染。
最簡單是直接在組件內(nèi)使用生命周期獲取數(shù)據(jù),然后存在組件內(nèi)部的 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 請看:
這種方式非常非常符合人的直覺,但最大的問題是:外部無法改變異步數(shù)據(jù),組件渲染后數(shù)據(jù)就無法再改變。這也是大部分人使用狀態(tài)管理維護(hù)異步數(shù)據(jù)的緣由。
下面我們看看如何使用 Redux 維護(hù)異步數(shù)據(jù)。
使用 Redux假設(shè)我們已經(jīng)使用了 Redux 中間件 redux-thunk,我們會(huì)有下面類似的代碼:
首先,我們會(huì)把字符串定義定義為常量到一個(gè) 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 中處理數(shù)據(jù),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 請看:
我們可以發(fā)現(xiàn),使用 Redux 管理異步數(shù)據(jù),代碼量激增,啰嗦冗余,模板代碼一堆,,不管開發(fā)效率還是開發(fā)體驗(yàn),亦或是可以維護(hù)性和可讀性,個(gè)人認(rèn)為,類似的 redux 這樣的解決方案并不優(yōu)雅。
下面我們看看如何使用 React Hooks 獲取異步數(shù)據(jù)。
使用 React Hooks我們使用 一個(gè)庫叫dahlia-rest 的 useFetch 獲取數(shù)據(jù),可以輕松的拿到數(shù)據(jù)的狀態(tài) { 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 狀態(tài)和錯(cuò)誤處理非常優(yōu)雅,也許你發(fā)現(xiàn)了,貌似這也和使用生命周期一樣,外部無法改變數(shù)據(jù)狀態(tài),其實(shí)不是的,下面重會(huì)點(diǎn)講講如何更新數(shù)據(jù)。
Hooks 更新異步數(shù)據(jù)使用 hooks 維護(hù)異步數(shù)據(jù),有三種方式更新異步數(shù)據(jù),這里用 dahlia-rest 舉例。
內(nèi)部 refetch這是最簡單的重新獲取數(shù)據(jù)的方式,通常,如果觸發(fā)更新的動(dòng)作和useFetch在統(tǒng)一組件內(nèi),可以使用這種方式。
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 請看:
通過更新依賴來重新獲取數(shù)據(jù),這也是常用的方式之一,因?yàn)樵诤芏鄻I(yè)務(wù)場景中,觸發(fā)動(dòng)作會(huì)在其他組件中,下面演示如何通過更新依賴觸發(fā)數(shù)據(jù)更新:
這里使用一個(gè)簡單的狀態(tài)管理庫維護(hù)依賴對象,狀態(tài)管理的完整文檔請看dahlia-store。
定義一個(gè) 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 請看:
你可以在任意地方,不管組件內(nèi)還是組件外,你都可以可以調(diào)用todoStore.updateParams更新依賴,從而實(shí)現(xiàn)數(shù)據(jù)更新。
注意:這里的依賴是個(gè)對象,你必須更新整個(gè)對象的引用,如果你只更新對象的屬性是無效的。
使用 fetcher有時(shí)候,你需要在組件外部重新獲取數(shù)據(jù),但useFetch 卻沒有任何可以被依賴的參數(shù),這時(shí)你可以使用 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 參數(shù),用法是:fetcher["name"].refetch(),這里的 refetch 和內(nèi)部 refetch 是同一個(gè)函數(shù),所以它也有 options 參數(shù)。
總結(jié)個(gè)人認(rèn)為,異步數(shù)據(jù)不應(yīng)該使用狀態(tài)管理來維護(hù),應(yīng)該放在組件內(nèi)。對于大多數(shù) web 應(yīng)用,狀態(tài)管理中的數(shù)據(jù)應(yīng)該是比較薄的一層,并且應(yīng)該避免在狀態(tài)管理中處理異步帶來的副作用。也許,Redux 默認(rèn)不支持處理異步數(shù)據(jù),是一個(gè)相當(dāng)有遠(yuǎn)見的決定。
我們發(fā)現(xiàn),使用 Hooks 管理異步數(shù)據(jù),代碼非常簡潔,有一種大道至簡感覺和返璞歸真感覺。幾行代碼就能寫完功能,為什么要搞出那么長的鏈路,搞那么繞的邏輯。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/104111.html
摘要:在項(xiàng)目開始之前,不能心急立刻去搭建,需要設(shè)定幾個(gè)步驟來開展,接下來大概的說一下我從技術(shù)選型到項(xiàng)目前端搭建好的整個(gè)生命周期。開發(fā)該項(xiàng)目的底層的內(nèi)容遠(yuǎn)不止這些,但由于公司制度規(guī)定,只能大概的闡述了在從接手到選型到搭建完畢這到的過程的做法和思考。 前段時(shí)間部門要基于一個(gè)系統(tǒng)的基礎(chǔ)上開發(fā)一個(gè)管理平臺,于是我接手了該平臺的重活,因?yàn)樯弦粋€(gè)平臺我用了vue搭建,所以這次想用react來搭建。在項(xiàng)目...
摘要:是分發(fā)器,是數(shù)據(jù)與邏輯處理器,會(huì)在注冊針對各個(gè)命令字的響應(yīng)回調(diào)函數(shù)。當(dāng)按如下方式觸發(fā)回調(diào)時(shí),回調(diào)函數(shù)具備事件的特性。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設(shè)計(jì)要點(diǎn)。本篇解釋 Shadow Widget 在 MVC、MVVM、Flux 框架之間如何做選擇。 showImg(https://segmentfault.com/img/bVOODj?w=380&h...
摘要:深入簡述在快速理解中,我簡單的介紹了的基礎(chǔ)內(nèi)容,本篇文章中,我們將再度深入。二修改我曾在快速理解中提起,為了解決模塊組件之間需要共享數(shù)據(jù)和數(shù)據(jù)可能被任意修改導(dǎo)致不可預(yù)料的結(jié)果的矛盾,團(tuán)隊(duì)創(chuàng)建了。 深入Redux 簡述 在快速理解redux中,我簡單的介紹了redux的基礎(chǔ)內(nèi)容,本篇文章中,我們將再度深入redux。 redux解決的問題 數(shù)據(jù)和函數(shù)的層層傳遞 多個(gè)組件同時(shí)修改某全局變...
摘要:前端日報(bào)精選在中的元素種類及性能優(yōu)化譯異步遞歸回調(diào)譯定位一個(gè)頁面阻塞問題的排查過程前端分享之的使用及單點(diǎn)登錄中文視頻如何用做好一個(gè)大型應(yīng)用云際個(gè)實(shí)用技巧眾成翻譯年一定不要錯(cuò)過的五本編程書籍年前端領(lǐng)域有哪些探索和實(shí)踐實(shí)現(xiàn)一個(gè)時(shí)光網(wǎng)掘金 2017-09-22 前端日報(bào) 精選 JavaScript 在 V8 中的元素種類及性能優(yōu)化【譯】異步遞歸:回調(diào)、Promise、Async[譯]HTML...
閱讀 3435·2021-10-20 13:49
閱讀 2809·2021-09-29 09:34
閱讀 3706·2021-09-01 11:29
閱讀 3088·2019-08-30 11:01
閱讀 853·2019-08-29 17:10
閱讀 891·2019-08-29 12:48
閱讀 2789·2019-08-29 12:40
閱讀 1363·2019-08-29 12:30