前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >6个React Hook最佳实践技巧

6个React Hook最佳实践技巧

作者头像
深度学习与Python
发布2020-11-23 10:33:11
2.5K0
发布2020-11-23 10:33:11
举报
文章被收录于专栏:深度学习与python

作者 | Nathan Sebhastian

译者 | 王强

策划 | 蔡芳芳

在过去,像状态和生命周期函数这样的 React 特性只适用于基于类的组件。基于函数的组件被称为哑(dumb)、瘦(skinny)或表示(presentational)组件,因为它们无法访问状态和生命周期函数。

但是自从 React Hooks 发布以来,基于函数的组件已升格为 React 的一等公民。它使函数组件能够以新的方式编写、重用和共享 React 代码。

在这篇文章中,我将分享 6 个关于 React Hooks 的技巧。你可以把它当作一份指南,在将 Hooks 实现到组件中时可以拿来参考。

1 遵守 Hooks 规则

这条规则看起来是句废话,但无论是新手还是经验丰富的 React 开发人员,都常常会忘记遵循 React Hooks 的规则。这些规则包括:

仅在顶级调用 Hooks

不要在循环、条件和嵌套函数内调用 Hooks。当你想有条件地使用某些 Hooks 时,请在这些 Hooks 中写入条件。

不要这样做:

代码语言:javascript
复制
if (name !== '') {
 useEffect(function persistForm() {
   localStorage.setItem('formData', name);
 });
}

相比之下,你应该这样做:

代码语言:javascript
复制
useEffect(function persistForm() {
  if (name !== '') {
    localStorage.setItem('formData', name);
  }
});

这条规则能确保每次渲染组件时都以相同的顺序调用 Hooks。这样一来,React 就能在多个 useState 和 useEffect 调用之间正确保留 Hooks 的状态。

仅从函数组件调用 Hooks

不要从常规 JavaScript 函数中调用 Hooks。仅从函数组件或自定义 Hooks 中调用 Hooks。

遵循这一条规则,可以确保组件中的所有状态逻辑在源代码中都能清晰可见。

2 使用 ESLint 的 React Hooks 插件

React 团队还创建了一个名为 eslint-plugin-react-hooks 的 ESLint 插件,以帮助开发人员在自己的项目中以正确的方式编写 React Hooks。这个插件能够帮助你在尝试运行应用程序之前捕获并修复 Hooks 错误。

它有两条简单的规则:

  • react-hooks/rules-of-hooks
  • react-hooks/exhaustive-deps

第一条规则只是强制你的代码符合我在第一个技巧中说明的 Hooks 规则。第二个规则,exhaustive-deps 用于实施 useEffect 的规则:effect 函数中引用的每个值也应出现在依赖项数组中。

例如,下面这个 userInfo 组件会触发 exhaustive-deps 警告,因为 userId 变量在 useEffect 内部被引用,但未在依赖项数组中传递:

代码语言:javascript
复制
function UserInfo({userId}) {
  const [user, setUser] = useState(null)
  useEffect(() => {
    getUser(userId).then(user => setUser(user))
  }, []) // no userId here
  return <div>User detail:</div>
}

尽管 exhaustive-deps 这条规则看起来很烦人,但它能帮助你避免由未列出的依赖项引发的错误。

3 以正确的顺序创建函数组件

当创建类组件时,遵循一定的顺序可以帮助你更好地维护和改进 React 应用程序代码。

首先调用构造器并启动状态。然后编写生命周期函数,接着编写与组件作业相关的所有函数。最后编写 render 方法:

代码语言:javascript
复制
const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};


const defaultProps = {
  text: 'Hello World',
};
class Link extends React.Component {
  static methodsAreOk() {
    return true;
  }
  constructor(props) {
    super(props)
    this.state = {
      user = null
    }
  }
  
  componentDidMount() {
    console.log('component did mount')
  }
  componentDidUpdate() {
    console.log('component did update')
  }
  componentWillUnmount() {
    console.log('component will unmount')
  }
  render() {
    return <a href={this.props.url}>{this.props.text}</a>
  }
}
Link.propTypes = propTypes
Link.defaultProps = defaultProps
export default Lin

编写函数组件时并没有构造器和生命周期函数,因此你可能会犯糊涂,因为这种结构并不像类组件里那样是强制的:

代码语言:javascript
复制
function App() {
  const [user, setUser] = useState(null);
  useEffect(() => {
    console.log("component is mounted");
  }, []);
  const [name, setName] = useState('');
  return <h1>React component order</h1>;
}

但就像类组件一样,为函数组件创建定义的结构能够改善项目的可读性。

建议先使用 useState Hook 声明状态变量,然后使用 useEffect Hook 编写订阅,接着编写与组件作业相关的其他函数。

最后,你得返回要由浏览器渲染的元素:

代码语言:javascript
复制
function App() {
  const [user, setUser] = useState(null);
  const [name, setName] = useState('');
  useEffect(() => {
    console.log("component is mounted");
  }, []);
  return <h1>React component order</h1>;
}

通过强制一种结构,可以让代码流在众多组件之间保持一致,看起来也比较亲切。

4 useState 的用法可以和类组件的状态完全一致,不只用于单个值

许多 useState 示例会向你展示如何通过声明多个变量来声明多个状态:

代码语言:javascript
复制
const [name, setName] = useState('John Doe');
const [email, setEmail] = useState('johndoe@email.com');
const [age, setAge] = useState(28);

但是 useState 实际上既可以处理数组也可以处理对象。你依旧可以将相关数据分组为一个状态变量,如以下示例所示:

代码语言:javascript
复制
const [user, setUser] = useState(
  { name: 'John Doe', email: 'john@email.com', age: 28 }
);

这里有一个警告。使用 useState 的更新函数更新状态时,以前的状态会替换为新状态。这与类组件的 this.setState 不同,后者的新类中,新状态会与旧状态合并:

代码语言:javascript
复制
const [user, setUser] = useState(
  { name: 'John', email: 'john@email.com', age: 28 }
);
setUser({ name: 'Nathan' });
// result { name: 'Nathan' }

为了保留以前的状态,你需要创建将当前状态值传递到自身中的回调函数来手动合并它。由于上面的示例已将 user 变量分配为状态值,因此可以将其传递给 setUser 函数,如下所示:

代码语言:javascript
复制
setUser((user) = > ({ ...user, name: 'Nathan' }));
// result is { name:'Nathan', email: 'john@email.com', age: 28 }

根据数据在应用程序生命周期中的变化情况,建议在各个值彼此独立时将状态拆分为多个变量。

但是对于某些情况,例如构建一个简单的表单,最好将状态分组在一起,以便更轻松地处理更改和提交数据。

简而言之,你需要在多个 useState 调用和单个 useState 调用之间保持平衡。

5 使用自定义 Hooks 共享应用程序逻辑

在构建应用程序时,你会注意到一些应用程序逻辑会在许多组件中一次又一次地使用。

随着 React Hooks 的发布,你可以将组件的逻辑提取到可重用的函数中作为自定义 Hooks,如我在以下文章中所展示的那样:

可扩展 React 项目的 6 个技巧和最佳实践:

https://blog.bitsrc.io/best-practices-and-tips-for-a-scalable-react-application-db708ae49227

你可以使用 Bit 之类的工具将 Hooks 发布到单个集合中,这样你就可以在不同的应用程序中安装和重用它们。它不需要你创建一个全新的“Hooks 库”项目,你可以一点点将新的 Hooks 从任何项目“推入”你的共享集合。

针对这个方法,唯一要强调的是你不能在类组件中使用 Hooks。所以如果你的项目中还有老式的类组件,就需要将它们转换为函数,或者使用其他可重用逻辑模式(HOC 或渲染 Props)。

使用 useContext 避免 prop drilling

prop-drilling 是 React 应用程序中的常见问题,指的是将数据从一个父组件向下传递,经过各层组,直到到达指定的子组件,而其他嵌套组件实际上并不需要它们。

考虑以下示例:

https://bit.dev/nsebhastian/tutorial-examples/prop-drill-example?example=5f941e4445728c001924150a

从示例中可以看到,即使 Hello 组件不需要 props,App 组件也会通过 Hello 组件将 name props 传递给 Greeting 组件。

React Context 是一项功能,它提供了一种通过组件树向下传递数据的方法,这种方法无需在组件之间手动传 props。父组件中定义的 React Context 的值可由其子级通过 useContext Hook 访问。

在下面的示例中,我将 name 数据(而非 props)传递给 Context Provider,给代码做了重构:

https://bit.dev/nsebhastian/tutorial-examples/prop-drill-example?example=5f941fae45728c001924150e

App 的任何子组件都可以通过 useContext Hook 访问数据。可以从文档中了解有关 useContext Hook 的更多信息:

https://reactjs.org/docs/hooks-reference.html#usecontext

6 总结

React Hooks 是 React 库的重要补充,因为它允许你用独一无二的方式编写、重用和共享 React 代码。

随着 Hooks 开始改变开发人员编写 React 组件的方式,需要一套新的编写 React Hooks 的最佳实践,以便多个团队之间更轻松地开发和协作。

虽然本文肯定还有遗漏的内容,但我希望以上分享的技巧能多少帮助你在项目中以正确的方式编写 React Hooks。

查看英文原文:

https://blog.bitsrc.io/best-practices-with-react-hooks-69d7e4af69a7


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 InfoQ 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档