React Use Hook 尝鲜

Posted GoldenaArcher

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Use Hook 尝鲜相关的知识,希望对你有一定的参考价值。

React Use Hook 尝鲜

最近继续在找处理 React 异步调用的方式……主要是现在需求比较复杂,用 cache query 的方式去实现有那么一丢丢的麻烦,又不是很想用额外的包,所以就想看看有没有比较好的一些处理方式。

当然,可以用到生产环境上的没找到,不过找到了一个还在试验阶段的新功能——use hook,毫不夸张地说,这个 hook 打破了 React 既有的 hook 规范。

React 团队终于想不出什么正常点的名字,所以直接用 use 了吗

⚠️:还在试验阶段,未来不确定会不会被删除,友情提示不要用到生产环境

具体的 proposal 和 discussion 在这里: RFC: First class support for promises and async/await,里面还有一些挺有趣的 hooks 和用法,比如说已经被 merged 的 useSuspenseQuery现在不工作的 export default async function ServerComponent() 等,总之这个 thread 感觉还是可以看看的。

基础用法

使用这个功能之前需要将 react 的版本设置为 experimental:


  "dependencies": 
    "react": "experimental",
    "react-dom": "experimental"
  

下面是一个基础的使用案例,目前在不借助第三方库的实现方式,异步的操作大概是借助 useEffectuseState 这样实现的:

import './App.css';
import  useEffect, useState  from 'react';

function App() 
  const [product, setProduct] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => 
    setLoading(true);
    fetch('https://dummyjson.com/products/1')
      .then((res) => res.json())
      .then((json) => setProduct(json))
      .catch((e) => console.error(e))
      .finally(() => setLoading(false));
  , []);

  return (
    <div className="App">
      loading && <h1>Is Loading</h1>
      !loading && JSON.stringify(product)
    </div>
  );


export default App;

在使用了 use 之后,可以简化成这样:

import './App.css';
import  use  from 'react';

const fetchProduct = fetch('https://dummyjson.com/products/1').then((res) =>
  res.json()
);

function App() 
  const product = use(fetchProduct);

  console.log(product);

  return <div className="App">JSON.stringify(product)</div>;


export default App;

我试着尝试了一下 log 了一下 product,然后发现,product 不存在先被设置为 undefined,随后在 API 调用成功后再被赋值的情况:

也就是说,使用 use hook 并不遵从 React 目前其他的 hook 的生命周期方式,即先渲染组件,再处理完异步操作后渲染页面的步骤,而是直接等待异步操作完成后,再渲染页面。

这也就是当我修改了一下 API,让异步调用失败的时候,页面直接就白屏的因素:

render 部分完全没有调用,API 一旦失败了,那么这个页面就挂了。

目前关于怎样正式处理 loading 和 error 还没有一个定论,只是其中一个成员是这么说的:

总结一下就是,当 use hook 处在一个薛定谔的状态时,会调用最近的 Suspense,当 use hook 调用失败时则会调用最近的 error boundary。

进阶之 Loading 与错误处理 的实现

使用 Suspense 和 error boundary 的部分,这里假设你知道 Suspense 和 error boundary,并知道怎么简单的实现这两个功能。

react 什么时候考虑吧 error boundary 的 hook 提上日程啊

loading 的处理

也就是利用 Suspense 的特性去实现,另外使用 Suspense 的好处在于,它会等到所有的 use hooks 都完成了之后,再渲染页面。

也就是说,并不需要使用 Promise.all(),只需要在 Suspense 中调用多个使用 use 的组件即可。

import './App.css';
import React,  use, Suspense  from 'react';

const fetchProduct = fetch('https://dummyjson.com/products/1').then((res) =>
  res.json()
);

const Data = () => 
  const product = use(fetchProduct);

  return <div>JSON.stringify(product)</div>;
;

export default Data;


function App() 
  return (
    <Suspense fallback=<div>loading</div>>
      <Data />
    </Suspense>
  );


export default App;

效果如下:

这样的实现也有好有坏吧,官方说目前的操作是调用最近的 Suspense,这个也的确会让 灵活处理 loading 状态 这一需求变得有些麻烦。

比如说有些情况下只是需要将 loading 传入一些表单中,让 UI 库去模拟拉取的状态,如 <Form loading=loading>, <Table loading=loading> 这种情况。去梳理 DOM 树,并且在最近的结点处理或事创立 Suspense 可能会有些麻烦。

使用 error boundary

error boundary 甚至是一个 class based component 才有的功能:

class ErrorBoundry extends 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;
  


const Data = () => 
  const product = use(fetchProduct);

  return <div>JSON.stringify(product)</div>;
;

export default Data;


function App() 
  return (
    <ErrorBoundry fallback=<div>Error</div>>
      <Suspense fallback=<div>loading</div>>
        <Data />
      </Suspense>
    </ErrorBoundry>
  );


export default App;

同理,去梳理 DOM 树,并且在最近的结点处理或事创立 error boundary 也可能会有些麻烦。

另一个不遵从常规的特性

React 官方文档中单独分出一个节点说在顶部使用 hooks:Only Call Hooks at the Top Level,原文如下:

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.

但是 use hook 打破了这条规范,如下面就是将 use hook 放在了 if 中:

import React,  use  from 'react';

const fetchProduct = fetch('https://dummyjson.com/pdd/1').then((res) =>
  res.json()
);

const Data = () => 
  const bool = false;
  if (bool) 
    const product = use(fetchProduct);

    return <div>JSON.stringify(product)</div>;
  

  return 'not fatched';
;

export default Data;

这其实也是另外一个我不太确定 use hook 到底什么时候会被并入到生产阶段的原因,就是这个 hook 确实会完全打破 React 之前固有的 hook 规范。

当然,对于开发来说,这也更加的灵活,使用起来更方便就是了。

以上是关于React Use Hook 尝鲜的主要内容,如果未能解决你的问题,请参考以下文章

React JS 多个提交按钮 react-hook-form

文本字段更改的值未在 OnSubmit 中更新 - React-Hook-Form 和 React Js

react-hook-form 的 DefaultValues 未将值设置为 React JS 中的输入字段

React.js useState hook 导致过多的重新渲染并且无法更新我的状态

[React] Use the React Effect Hook in Function Components

从 react-bootstrap-table2 的列渲染器中使用 Location hook 获取位置路径