欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React競(jìng)態(tài)條件Race Condition實(shí)例詳解

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

競(jìng)態(tài)條件

Race Condition,中文譯為競(jìng)態(tài)條件,旨在描述一個(gè)系統(tǒng)或者進(jìn)程的輸出,依賴于不受控制事件的出現(xiàn)順序或者出現(xiàn)時(shí)機(jī)。

舉個(gè)簡(jiǎn)單的例子:

if (x == 5) // The "Check"
{
   y = x * 2; // The "Act"
   // 如果其他的線程在 "if (x == 5)" and "y = x * 2" 執(zhí)行之間更改了 x 的值
   // y 就可能不等于 10.
}

你可能想,JavaScript 是單線程,怎么可能出現(xiàn)這個(gè)問(wèn)題?

React 與競(jìng)態(tài)條件

確實(shí)如此,但前端有異步渲染,所以競(jìng)態(tài)條件依然有可能出現(xiàn),我們舉個(gè) React 中常見的例子。

這是一個(gè)非常典型的數(shù)據(jù)獲取代碼:

class Article extends Component {
  state = {
    article: null
  };
  componentDidMount() {
    this.fetchData(this.props.id);
  }
  async fetchData(id) {
    const article = await API.fetchArticle(id);
    this.setState({ article });
  }
  // ...
}

看起來(lái)沒什么問(wèn)題,但這段代碼還沒有實(shí)現(xiàn)數(shù)據(jù)更新,我們?cè)俑囊幌拢?/p>

class Article extends Component {
  state = {
    article: null
  };
  componentDidMount() {
    this.fetchData(this.props.id);
  }
  componentDidUpdate(prevProps) {
    if (prevProps.id !== this.props.id) {
      this.fetchData(this.props.id);
    }
  }
  async fetchData(id) {
    const article = await API.fetchArticle(id);
    this.setState({ article });
  }
  // ...
}

當(dāng)組件傳入新的 id 時(shí),我們根據(jù)新的 id 請(qǐng)求數(shù)據(jù),然后 setState 最新獲取的數(shù)據(jù)。

這時(shí)就可能出現(xiàn)競(jìng)態(tài)條件,比如用戶選完立刻點(diǎn)擊下一頁(yè),我們請(qǐng)求 id 為 1 的數(shù)據(jù),緊接著請(qǐng)求 id 為 2 的數(shù)據(jù),但因?yàn)榫W(wǎng)絡(luò)或者接口處理等原因,id為 2 的接口提前返回,便會(huì)先展示 id 為 2 的數(shù)據(jù),再展示 id 為 1 的數(shù)據(jù),這就導(dǎo)致了錯(cuò)誤。

我們可以想想遇到這種問(wèn)題的場(chǎng)景,比如類似于百度的搜索功能,切換 tab 等場(chǎng)景,雖然我們也可以使用諸如 debounce 的方式來(lái)緩解,但效果還是會(huì)差點(diǎn),比如使用 debounce,用戶在輸入搜索詞的時(shí)候,展示內(nèi)容會(huì)長(zhǎng)期處于空白狀態(tài),對(duì)于用戶體驗(yàn)而言,我們可以做的更好。

那么我們?cè)撊绾谓鉀Q呢?一種是在切換的時(shí)候取消請(qǐng)求,還有一種是借助一個(gè)布爾值來(lái)判斷是否需要更新,比如這樣:

function Article({ id }) {
  const [article, setArticle] = useState(null);
  useEffect(() => {
    let didCancel = false;
    async function fetchData() {
      const article = await API.fetchArticle(id);
      // 如果 didCancel 為 true 說(shuō)明用戶已經(jīng)取消了
      if (!didCancel) {
        setArticle(article);
      }
    }
    fetchData();
    // 執(zhí)行下一個(gè) effect 之前會(huì)執(zhí)行
    return () => {
      didCancel = true;
    };
  }, [id]);
  // ...
}

當(dāng)然你也可以用 ahooks 中的 useRequest,它的內(nèi)部有一個(gè) ref 變量記錄最新的 promise,也可以解決 Race Condition 的問(wèn)題:

function Article({ id }) {
  const { data, loading, error} = useRequest(() => fetchArticle(id), {
  	refreshDeps: [id]
  });
  // ...
}

效果演示

問(wèn)題復(fù)現(xiàn)

為了方便大家自己測(cè)試這個(gè)問(wèn)題,我們提供相對(duì)完整的代碼。以 《Avoiding Race Conditions when Fetching Data with React Hooks》中的例子為例,出現(xiàn) Race Condition 問(wèn)題的代碼如下:

const fakeFetch = person => {
  return new Promise(res => {
    setTimeout(() => res(`${person}'s data`), Math.random() * 5000);
  });
};
const App = () => {
  const [data, setData] = useState('');
  const [loading, setLoading] = useState(false);
  const [person, setPerson] = useState(null);
  useEffect(() => {
    setLoading(true);
    fakeFetch(person).then(data => {
      	setData(data);
      	setLoading(false);
    });
  }, [person]);
    const handleClick = (name) => () => {
    	setPerson(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>
      {person && (
        <Fragment>
          <h1>{person}</h1>
          <p>{loading ? 'Loading...' : data}</p>
        </Fragment>
      )}
    </Fragment>
  );
};

我們實(shí)現(xiàn)了一個(gè) fakeFetch函數(shù),用于模擬接口的返回,具體返回的時(shí)間為 Math.random() * 5000),用于模擬數(shù)據(jù)的隨機(jī)返回。

實(shí)現(xiàn)效果如下:

從效果圖中可以看到,我們按順序點(diǎn)擊了 Nick、Deb、Joe,理想情況下,結(jié)果應(yīng)該顯示 Joe's Data,但最終顯示的數(shù)據(jù)為最后返回的 Nick's Data。

布爾值解決

現(xiàn)在,我們嘗試用一個(gè) canceled 布爾值解決:

const App = () => {
  const [data, setData] = useState('');
  const [loading, setLoading] = useState(false);
  const [person, setPerson] = useState(null);
  useEffect(() => {
    let canceled = false;
    setLoading(true);
    fakeFetch(person).then(data => {
      if (!canceled) {
        setData(data);
        setLoading(false);
      }
    });
    return () => (canceled = true);
  }, [person]);
  return (
    <Fragment>
      <button onClick={() => setPerson('Nick')}>Nick's Profile</button>
      <button onClick={() => setPerson('Deb')}>Deb's Profile</button>
      <button onClick={() => setPerson('Joe')}>Joe's Profile</button>
      {person && (
      <Fragment>
        <h1>{person}</h1>
        <p>{loading ? 'Loading...' : data}</p>
      </Fragment>
    )}
    </Fragment>
  );
};

實(shí)現(xiàn)效果如下:

即便接口沒有按照順序返回,依然不影響最終顯示的數(shù)據(jù)。

useRequest 解決

我們也可以借助 ahooksuseRequest 方法,修改后的代碼如下:

const App2 = () => {
  const [person, setPerson] = useState('Nick');
  const { data, loading} = useRequest(() => fakeFetch(person), {
    refreshDeps: [person],
  });
  const handleClick = (name) => () => {
    setPerson(name)
  }
  return (
    <Fragment>
      <button onClick={handleClick('Nick')}>Nick's Profile</button>
      <button onClick={handleClick('Deb')}>Deb's Profile</button>
      <button onClick={() => setPerson('Joe')}>Joe's Profile</button>
      {person && (
      <Fragment>
        <h1>{person}</h1>
        <p>{loading ? 'Loading...' : data}</p>
      </Fragment>
    )}
    </Fragment>
  );
};

代碼效果如上,就不重復(fù)錄制了。

考慮到部分同學(xué)可能會(huì)對(duì) useRequest 的使用感到困惑,我們簡(jiǎn)單介紹一下 useRequest的使用:

useRequest 的第一個(gè)參數(shù)是一個(gè)異步函數(shù),在組件初次加載時(shí),會(huì)自動(dòng)觸發(fā)該函數(shù)執(zhí)行。同時(shí)自動(dòng)管理該異步函數(shù)的 loading 、 data 、 error 等狀態(tài)。

useRequest 同樣提供了一個(gè) options.refreshDeps 參數(shù),當(dāng)它的值變化后,會(huì)重新觸發(fā)請(qǐng)求。

const [userId, setUserId] = useState('1');
const { data, run } = useRequest(() => getUserSchool(userId), {
  refreshDeps: [userId],
});

上面的示例代碼,useRequest 會(huì)在初始化和 userId 變化時(shí),觸發(fā)函數(shù)執(zhí)行。與下面代碼實(shí)現(xiàn)功能完全一致:

const [userId, setUserId] = useState('1');
const { data, refresh } = useRequest(() => getUserSchool(userId));
useEffect(() => {
  refresh();
}, [userId]);

Suspense

這篇之所以講 Race Condition,主要還是為了引入講解 Suspense,借助 Suspense,我們同樣可以解決 Race Condition:

// 實(shí)現(xiàn)參考的 React 官方示例:https://codesandbox.io/s/infallible-feather-xjtbu
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;
      }
    }
  };
}
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>
  );
};

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

相關(guān)文章

  • react無(wú)限滾動(dòng)組件的實(shí)現(xiàn)示例

    react無(wú)限滾動(dòng)組件的實(shí)現(xiàn)示例

    本文主要介紹了react無(wú)限滾動(dòng)組件的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • React如何使用create-react-app創(chuàng)建react項(xiàng)目

    React如何使用create-react-app創(chuàng)建react項(xiàng)目

    這篇文章主要介紹了React如何使用create-react-app創(chuàng)建react項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 詳解React如何獲取狀態(tài)的舊值

    詳解React如何獲取狀態(tài)的舊值

    最近剛開始接觸 React,突然腦海出現(xiàn)一個(gè)問(wèn)題,React中怎么在狀態(tài)更新時(shí)獲取它的舊值,特別是如果你之前用過(guò) Vue,你可能會(huì)想知道 React 中有沒有類似 Vue 的 watch 屬性,那么react中怎么實(shí)現(xiàn)呢?本文就給大家介紹一下React如何獲取狀態(tài)的舊值,需要的朋友可以參考下
    2024-07-07
  • React Render Props共享代碼技術(shù)

    React Render Props共享代碼技術(shù)

    render props是指一種在 React 組件之間使用一個(gè)值為函數(shù)的 prop 共享代碼的技術(shù)。簡(jiǎn)單來(lái)說(shuō),給一個(gè)組件傳入一個(gè)prop,這個(gè)props是一個(gè)函數(shù),函數(shù)的作用是用來(lái)告訴這個(gè)組件需要渲染什么內(nèi)容,那么這個(gè)prop就成為render prop
    2023-01-01
  • react電商商品列表的實(shí)現(xiàn)流程詳解

    react電商商品列表的實(shí)現(xiàn)流程詳解

    這篇文章主要介紹了react實(shí)現(xiàn)電商商品列表的流程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • React+Antd+Redux實(shí)現(xiàn)待辦事件的方法

    React+Antd+Redux實(shí)現(xiàn)待辦事件的方法

    這篇文章主要介紹了React+Antd+Redux實(shí)現(xiàn)待辦事件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • React-Native中一些常用組件的用法詳解(二)

    React-Native中一些常用組件的用法詳解(二)

    這篇文章主要跟大家介紹了關(guān)于React-Native中一些常用組件的用法的相關(guān)資料,其中詳細(xì)介紹了關(guān)于ScrollView組件、ListView組件、Navigator組件、TableBarIOS組件以及網(wǎng)絡(luò)請(qǐng)求等相關(guān)內(nèi)容,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-06-06
  • React中代碼分割的4種實(shí)現(xiàn)方式

    React中代碼分割的4種實(shí)現(xiàn)方式

    雖然一直有做react相關(guān)的優(yōu)化,按需加載、dll 分離、服務(wù)端渲染,但是從來(lái)沒有從路由代碼分割這一塊入手過(guò),所以下面這篇文章主要給大家介紹了關(guān)于React中代碼分割的4種實(shí)現(xiàn)方式,需要的朋友可以參考下
    2022-01-01
  • React中實(shí)現(xiàn)插槽效果的方案詳解

    React中實(shí)現(xiàn)插槽效果的方案詳解

    在React中是沒有插槽的概念的, 或者說(shuō)在React中是不需要插槽的, 因?yàn)镽eact對(duì)于這種需要插槽的情況非常靈活,本文給大家分享兩種方案實(shí)現(xiàn),分別是children實(shí)現(xiàn)插槽和props實(shí)現(xiàn)插槽,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-09-09
  • react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能

    react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能

    這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)簡(jiǎn)單的拖拽功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論