最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React?Suspense解決競(jìng)態(tài)條件詳解

 更新時(shí)間:2022年11月10日 08:38:14   作者:冴羽  
這篇文章主要為大家介紹了React?Suspense解決競(jìng)態(tài)條件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在上一篇《React 之 Race Condition》中,我們最后引入了 Suspense 來(lái)解決競(jìng)態(tài)條件問(wèn)題,本篇我們來(lái)詳細(xì)講解一下 Suspense。

Suspense

React 16.6 新增了 <Suspense> 組件,讓你可以“等待”目標(biāo)代碼加載,并且可以直接指定一個(gè)加載的界面(像是個(gè) spinner),讓它在用戶等待的時(shí)候顯示。

目前,Suspense 僅支持的使用場(chǎng)景是:通過(guò) React.lazy 動(dòng)態(tài)加載組件

const ProfilePage = React.lazy(() => import('./ProfilePage')); // 懶加載
// 在 ProfilePage 組件處于加載階段時(shí)顯示一個(gè) spinner
<Suspense fallback={<Spinner />}>
  <ProfilePage />
</Suspense>

執(zhí)行機(jī)制

但這并不意味著 Suspense 不可以單獨(dú)使用,我們可以寫個(gè) Suspense 單獨(dú)使用的例子,不過(guò)目前使用起來(lái)會(huì)有些麻煩,但相信 React 官方會(huì)持續(xù)優(yōu)化這個(gè) API。

let data, promise;
function fetchData() {
  if (data) return data;
  promise = new Promise(resolve => {
    setTimeout(() => {
      data = 'data fetched'
      resolve()
    }, 3000)
  })
  throw promise;
}
function Content() {
  const data = fetchData();
  return <p>{data}</p>
}
function App() {
  return (
    <Suspense fallback={'loading data'}>
      <Content />
    </Suspense>
  )
}

這是一個(gè)非常簡(jiǎn)單的使用示例,但卻可以用來(lái)解釋 Suspense 的執(zhí)行機(jī)制。

最一開始 <Content> 組件會(huì) throw 一個(gè) promise,React 會(huì)捕獲這個(gè)異常,發(fā)現(xiàn)是 promise 后,會(huì)在這個(gè) promise 上追加一個(gè) then 函數(shù),在 then 函數(shù)中執(zhí)行 Suspense 組件的更新,然后展示 fallback 內(nèi)容。

等 fetchData 中的 promise resolve 后,會(huì)執(zhí)行追加的 then 函數(shù),觸發(fā) Suspense 組件的更新,此時(shí)有了 data 數(shù)據(jù),因?yàn)闆](méi)有異常,React 會(huì)刪除 fallback 組件,正常展示 <Content /> 組件。

實(shí)際應(yīng)用

如果我們每個(gè)請(qǐng)求都這樣去寫,代碼會(huì)很冗余,雖然有 react-cache 這個(gè) npm 包,但上次更新已經(jīng)是 4 年之前了,不過(guò)通過(guò)查看包源碼以及參考 React 官方的示例代碼,在實(shí)際項(xiàng)目中,我們可以這樣去寫:

// 1. 通用的 wrapPromise 函數(shù)
function wrapPromise(promise) {
  let status = "pending";
  let result;
  let suspender = promise.then(
    r => {
      status = "success";
      result = r;
    },
    e => {
      status = "error";
      result = e;
    }
  );
  return {
    read() {
      if (status === "pending") {
        throw suspender;
      } else if (status === "error") {
        throw result;
      } else if (status === "success") {
        return result;
      }
    }
  };
}
// 這里我們模擬了請(qǐng)求過(guò)程
const fakeFetch = () => {
  return new Promise(res => {
    setTimeout(() => res('data fetched'), 3000);
  });
};
// 2. 在渲染前發(fā)起請(qǐng)求
const resource = wrapPromise(fakeFetch());
function Content() {
  // 3. 通過(guò) resource.read() 獲取接口返回結(jié)果
  const data = resource.read();
  return <p>{data}</p>
}
function App() {
  return (
    <Suspense fallback={'loading data'}>
      <Content />
    </Suspense>
  )
}

在這段代碼里,我們聲明了一個(gè) wrapPromise 函數(shù),它接收一個(gè) promise,比如 fetch 請(qǐng)求。函數(shù)返回一個(gè)帶有 read 方法的對(duì)象,這是因?yàn)榉庋b成方法后,代碼可以延遲執(zhí)行,我們就可以在 Suspense 組件更新的時(shí)候再執(zhí)行方法,從而獲取最新的返回結(jié)果。

函數(shù)內(nèi)部記錄了三種狀態(tài),pending、success、error,根據(jù)狀態(tài)返回不同的內(nèi)容。

你可能會(huì)想,如果我們還要根據(jù) id 之類的數(shù)據(jù)點(diǎn)擊請(qǐng)求數(shù)據(jù)呢?使用 Suspense 該怎么做呢?React 官方文檔也給了示例代碼:

const fakeFetch = (id) => {
  return new Promise(res => {
    setTimeout(() => res(`${id} data fetched`), 3000);
  });
};
// 1. 依然是直接請(qǐng)求數(shù)據(jù)
const initialResource = wrapPromise(fakeFetch(1));
function Content({resource}) {
  // 3. 通過(guò) resource.read() 獲取接口返回結(jié)果
  const data = resource.read();
  return <p>{data}</p>
}
function App() {
  // 2. 將 wrapPromise 返回的對(duì)象作為 props 傳遞給組件
  const [resource, setResource] = useState(initialResource);
  // 4. 重新請(qǐng)求
  const handleClick = (id) => () => {
    setResource(wrapPromise(fakeFetch(id)));
  }
  return (
    <Fragment>
      <button onClick={handleClick(1)}>tab 1</button>
      <button onClick={handleClick(2)}>tab 2</button>
      <Suspense fallback={'loading data'}>
        <Content resource={resource} />
      </Suspense>
    </Fragment>
  )
}

好處:請(qǐng)求前置

使用 Suspense 一個(gè)非常大的好處就是請(qǐng)求是一開始就執(zhí)行的?;叵脒^(guò)往的發(fā)送請(qǐng)求的時(shí)機(jī),我們都是在 compentDidMount 的時(shí)候再請(qǐng)求的,React 是先渲染的節(jié)點(diǎn)再發(fā)送的請(qǐng)求,然而使用 Suspense,我們是先發(fā)送請(qǐng)求再渲染的節(jié)點(diǎn),這就帶來(lái)了體驗(yàn)上的提升。

尤其當(dāng)請(qǐng)求多個(gè)接口的時(shí)候,借助 Suspense,我們可以實(shí)現(xiàn)接口并行處理以及提早展現(xiàn),舉個(gè)例子:

function fetchData(id) {
  return {
    user: wrapPromise(fakeFetchUser(id)),
    posts: wrapPromise(fakeFetchPosts(id))
  };
}
const fakeFetchUser = (id) => {
  return new Promise(res => {
    setTimeout(() => res(`user ${id} data fetched`), 5000 * Math.random());
  });
};
const fakeFetchPosts = (id) => {
  return new Promise(res => {
    setTimeout(() => res(`posts ${id} data fetched`), 5000 * Math.random());
  });
};
const initialResource = fetchData(1);
function User({resource}) {
  const data = resource.user.read();
  return <p>{data}</p>
}
function Posts({resource}) {
  const data = resource.posts.read();
  return <p>{data}</p>
}
function App() {
  const [resource, setResource] = useState(initialResource);
  const handleClick = (id) => () => {
    setResource(fetchData(id));
  }
  return (
    <Fragment>
      <p><button onClick={handleClick(Math.ceil(Math.random() * 10))}>next user</button></p>
      <Suspense fallback={'loading user'}>
        <User resource={resource} />
        <Suspense fallback={'loading posts'}>
          <Posts resource={resource} />
        </Suspense>
      </Suspense>
    </Fragment>
  )
}

在這個(gè)示例代碼中,user 和 posts 接口是并行請(qǐng)求的,如果 posts 接口提前返回,而 user 接口還未返回,會(huì)等到 user 接口返回后,再一起展現(xiàn),但如果 user 接口提前返回,posts 接口后返回,則會(huì)先展示 user 信息,然后顯示 loading posts,等 posts 接口返回,再展示 posts 內(nèi)容。

這聽起來(lái)好像沒(méi)什么,但是想想如果我們是以前會(huì)怎么做,我們可能會(huì)用一個(gè) Promise.all 來(lái)實(shí)現(xiàn),但是 Promise.all 的問(wèn)題就在于必須等待所有接口返回才會(huì)執(zhí)行,而且如果其中有一個(gè) reject 了,都會(huì)走向 catch 邏輯。使用 Suspense,我們可以做到更好的展示效果。

好處:解決競(jìng)態(tài)條件

使用 Suspense 可以有效的解決 Race Conditions(競(jìng)態(tài)條件) 的問(wèn)題,關(guān)于 Race Conditions 可以參考《React 之 Race Condition》

Suspense 之所以能夠有效的解決 Race Conditions 問(wèn)題,就在于傳統(tǒng)的實(shí)現(xiàn)中,我們需要考慮 setState 的正確時(shí)機(jī),執(zhí)行順序是:1. 請(qǐng)求數(shù)據(jù) 2. 數(shù)據(jù)返回 3. setState 數(shù)據(jù)

而在 Suspense 中,我們請(qǐng)求后,立刻就設(shè)置了 setState,然后就只用等待請(qǐng)求返回,React 執(zhí)行 Suspense 的再次更新就好了,執(zhí)行順序是:1. 請(qǐng)求數(shù)據(jù) 2. setState 數(shù)據(jù) 3. 數(shù)據(jù)返回 4. Suspense 重新渲染,所以大大降低了出錯(cuò)的概率。

const fakeFetch = person => {
  return new Promise(res => {
    setTimeout(() => res(`${person}'s data`), Math.random() * 5000);
  });
};
function fetchData(userId) {
  return wrapPromise(fakeFetch(userId))
}
const initialResource = fetchData('Nick');
function User({ resource }) {
  const data = resource.read();
  return <p>{ data }</p>
}
const App = () => {
  const [person, setPerson] = useState('Nick');
  const [resource, setResource] = useState(initialResource);
  const handleClick = (name) => () => {
    setPerson(name)
    setResource(fetchData(name));
  }
  return (
    <Fragment>
      <button onClick={handleClick('Nick')}>Nick's Profile</button>
      <button onClick={handleClick('Deb')}>Deb's Profile</button>
	    <button onClick={handleClick('Joe')}>Joe's Profile</button>
      <Fragment>
        <h1>{person}</h1>
        <Suspense fallback={'loading'}>
          <User resource={resource} />
        </Suspense>
      </Fragment>
    </Fragment>
  );
};

錯(cuò)誤處理

注意我們使用的 wrapPromise 函數(shù):

function wrapPromise(promise) {
	// ...
  return {
    read() {
      if (status === "pending") {
        throw suspender;
      } else if (status === "error") {
        throw result;
      } else if (status === "success") {
        return result;
      }
    }
  };
}

當(dāng) status 為 error 的時(shí)候,會(huì) throw result 出來(lái),如果 throw 是一個(gè) promise,React 可以處理,但如果只是一個(gè) error,React 就處理不了了,這就會(huì)導(dǎo)致渲染出現(xiàn)問(wèn)題,所以我們有必要針對(duì) status 為 error 的情況進(jìn)行處理,React 官方文檔也提供了方法,那就是定義一個(gè)錯(cuò)誤邊界組件:

// 定義一個(gè)錯(cuò)誤邊界組件
class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  static getDerivedStateFromError(error) {
    return {
      hasError: true,
      error
    };
  }
  render() {
    if (this.state.hasError) {
      return this.props.fallback;
    }
    return this.props.children;
  }
}
function App() {
  // ...
  return (
    <Fragment>
      <button onClick={handleClick(1)}>tab 1</button>
      <button onClick={handleClick(2)}>tab 2</button>
      <ErrorBoundary fallback={<h2>Could not fetch posts.</h2>}>
        <Suspense fallback={'loading data'}>
          <Content resource={resource} />
        </Suspense>
      </ErrorBoundary>
    </Fragment>
  )
}

當(dāng) <Content /> 組件 throw 出 error 的時(shí)候,就會(huì)被 <ErrorBoundary />組件捕獲,然后展示 fallback 的內(nèi)容。

源碼

那 Suspense 的源碼呢?我們查看 React.js 的源碼

import {
  REACT_SUSPENSE_TYPE
} from 'shared/ReactSymbols';
export {
  REACT_SUSPENSE_TYPE as Suspense
};

再看下shared/ReactSymbols的源碼:

export const REACT_SUSPENSE_TYPE: symbol = Symbol.for('react.suspense');

所以當(dāng)我們寫一個(gè) Suspense 組件的時(shí)候:

<Suspense fallback={'loading data'}>
  <Content />
</Suspense>
// 被轉(zhuǎn)譯為
React.createElement(Suspense, {
  fallback: 'loading data'
}, React.createElement(Content, null));

createElement 傳入的 Suspense 就只是一個(gè)常量而已,具體的處理邏輯會(huì)在以后的文章中慢慢講解。

React 系列

以上就是React Suspense解決競(jìng)態(tài)條件詳解的詳細(xì)內(nèi)容,更多關(guān)于React Suspense競(jìng)態(tài)條件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • react-router-dom的使用說(shuō)明

    react-router-dom的使用說(shuō)明

    這篇文章主要介紹了react-router-dom的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React中的useEffect(副作用)介紹

    React中的useEffect(副作用)介紹

    這篇文章主要介紹了React中的useEffect(副作用),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • React替換傳統(tǒng)拷貝方法的Immutable使用

    React替換傳統(tǒng)拷貝方法的Immutable使用

    Immutable.js出自Facebook,是最流行的不可變數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)之一。它實(shí)現(xiàn)了完全的持久化數(shù)據(jù)結(jié)構(gòu),使用結(jié)構(gòu)共享。所有的更新操作都會(huì)返回新的值,但是在內(nèi)部結(jié)構(gòu)是共享的,來(lái)減少內(nèi)存占用
    2023-02-02
  • 詳解React中如何拆分組件

    詳解React中如何拆分組件

    這篇文章主要為大家詳細(xì)介紹了React中拆分組件的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們掌握React有一定的幫助,需要的小伙伴可以參考一下
    2023-12-12
  • react-native動(dòng)態(tài)切換tab組件的方法

    react-native動(dòng)態(tài)切換tab組件的方法

    在APP中免不了要使用tab組件,有的是tab切換,也有的是tab分類切換.這篇文章主要介紹了react-native動(dòng)態(tài)切換tab組件的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-07-07
  • React報(bào)錯(cuò)map()?is?not?a?function詳析

    React報(bào)錯(cuò)map()?is?not?a?function詳析

    這篇文章主要介紹了React報(bào)錯(cuò)map()?is?not?a?function詳析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • React-Native做一個(gè)文本輸入框組件的實(shí)現(xiàn)代碼

    React-Native做一個(gè)文本輸入框組件的實(shí)現(xiàn)代碼

    這篇文章主要介紹了React-Native做一個(gè)文本輸入框組件的實(shí)現(xiàn)代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-08-08
  • 快速創(chuàng)建React項(xiàng)目并配置webpack

    快速創(chuàng)建React項(xiàng)目并配置webpack

    這篇文章主要介紹了創(chuàng)建React項(xiàng)目并配置webpack,在這里需要注意,Create?React?App?requires?Node?14?or?higher.需要安裝高版本的node,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-01-01
  • react路由v6版本NavLink的兩個(gè)小坑及解決

    react路由v6版本NavLink的兩個(gè)小坑及解決

    這篇文章主要介紹了react路由v6版本NavLink的兩個(gè)小坑及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 使用React實(shí)現(xiàn)輪播效果組件示例代碼

    使用React實(shí)現(xiàn)輪播效果組件示例代碼

    React剛出來(lái)不久,組件還比較少,不像jquery那樣已經(jīng)有很多現(xiàn)成的插件了,于是自己寫了一個(gè)基于React的輪播效果組件,現(xiàn)在分享給大家,有需要的可以參考借鑒。
    2016-09-09

最新評(píng)論

南丹县| 承德市| 双桥区| 绥德县| 南皮县| 宁阳县| 休宁县| 铜川市| 长葛市| 蓝山县| 南充市| 资讯 | 万载县| 万全县| 依安县| 梅州市| 柞水县| 象山县| 富裕县| 阿克| 汾阳市| 离岛区| 闽清县| 固阳县| 灯塔市| 浪卡子县| 肇州县| 涞源县| 林芝县| 普陀区| 吴桥县| 宜都市| 阜康市| 诸城市| 理塘县| 中方县| 博湖县| 五大连池市| 平原县| 甘南县| 玉环县|