当React.js 16.8发布时,我们有机会使用React Hooks。 挂钩使我们能够使用函数编写功能齐全的组件。 我们可以使用所有React.js功能并以更方便的方式进行操作。
许多人不同意胡克斯的构想。 在本文中,我想介绍一下React Hooks给您的一些重要优点,以及为什么我们需要使用Hooks进行编写。
我不会谈论如何使用钩子。 对于示例来说,它不是很重要。 如果您想阅读有关此主题的内容,可以使用官方文档 。 另外,如果您对这个主题感兴趣,我会写更多关于Hooks的文章。
钩子使我们可以轻松地重用我们的代码
让我们想象一个呈现简单表单的组件。 它可以显示一些输入,并允许我们更改其值。
使用类符号,将出现以下内容:
class Form extends React.Component { state = { // Fields values fields: {}, }; render() { return ( <form> {/* Inputs render */} </form> ); }; }
现在让我们想象一下,每当字段值更改时,我们希望将它们自动保存到后端。 我建议跳过诸如shallowEqual
和shallowEqual
之类的外部函数的定义。
class Form extends React.Component { constructor(props) { super(props); this.saveToDraft = debounce(500, this.saveToDraft); }; state = { // Fields values fields: {}, // Draft saving meta draft: { isSaving: false, lastSaved: null, }, }; saveToDraft = (data) => { if (this.state.isSaving) { return; } this.setState({ isSaving: true, }); makeSomeAPICall().then(() => { this.setState({ isSaving: false, lastSaved: new Date(), }) }); } componentDidUpdate(prevProps, prevState) { if (!shallowEqual(prevState.fields, this.state.fields)) { this.saveToDraft(this.state.fields); } } render() { return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }; }
与钩子相同的组件:
const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }
如我们所见,这里没有太大的区别。 我们用useState
钩子替换了this.state
, useState
草稿保存在useEffect
钩子中。
我想在这里显示的区别是(也存在另一个区别,但我将集中讨论这一点):我们可以轻松地从组件中提取此代码并在其他地方使用它:
// useDraft hook can be used in any other component const useDraft = (fields) => { const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return [draftIsSaving, draftLastSaved]; } const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, draftLastSaved] = useDraft(fields); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }
我们可以在其他组件中使用useDraft
钩子! 当然,这是一个非常简单的示例,但是代码重用非常重要,并且该示例说明了使用Hooks多么容易。
钩子使我们可以更直观地编写组件
让我们想象一个类组件的渲染,例如,一个聊天屏幕,聊天列表和消息表单。 像这样:
class ChatApp extends React.Component { state = { currentChat: null, }; handleSubmit = (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${this.state.currentChat}`); }); }; render() { return ( <Fragment> <ChatsList changeChat={currentChat => { this.setState({ currentChat }); }} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={this.handleSubmit} /> </Fragment> ); }; }
然后想象我们的用户使用此聊天组件:
- 他们打开聊天室1
- 他们发送一条消息(让我们想象一些慢速的网络)
- 他们打开聊天室2
- 他们看到有关其消息的警报:
但是他们已经向第二个聊天室发送了一条消息,它是怎么发生的? 这是因为类方法使用当前值,而不是我们启动消息请求时的值。 像这样的简单组件没什么大不了的,但是它可能是更复杂系统中的错误的来源。
另一方面,功能组件以其他方式起作用:
const ChatApp = () => { const [currentChat, setCurrentChat] = useState(null); const handleSubmit = useCallback( (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${currentChat}`); }); }, [currentChat] ); render() { return ( <Fragment> <ChatsList changeChat={setCurrentChat} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={handleSubmit} /> </Fragment> ); }; }
假设我们的用户:
- 他们打开聊天室1
- 他们发送一条消息(让我们想象一些慢速的网络)
- 他们打开聊天室2
- 他们看到有关其消息的警报:
好,有什么变化? 现在,我们正在使用一个在渲染时刻捕获的值。 每当currentChat
更改时,我们都会创建一个新的handleSubmit
。 它使我们忘记了未来的变化,而现在就思考。
每个组件都会渲染以捕获其使用的所有内容 。
挂钩使组件的生命周期消失了
此原因与前一个原因强烈相交。 React是一个声明性的UI库。 声明性使UI创建和处理方式更加容易。 它使我们忘记了命令性的DOM更改。
即使这样,当我们使用类时,我们也面临着组件的生命周期。 看起来像这样:
- 安装方式
- 更新(
state
或props
更改时) - 正在卸载
似乎很方便,但我坚信这仅仅是因为我们的习惯。 它不像React。
取而代之的是,功能组件使我们能够编写组件的代码,而无需考虑生命周期。 我们只考虑同步 。 我们编写的函数使我们的UI从输入道具和内部状态开始。
最初, useEffect
挂钩似乎替代了componentDidMount
, componentDidUpdate
和其他生命周期方法。 但这不是这样。 当我们使用useEffect
我们对React说:“嘿,在渲染我的组件之后进行设置”。
这是有关useEffect的大型文章中的一个很好的示例:
- 反应:当状态为
0
时,给我UI。 - 您的组件:
- 这是渲染结果:
<p>You clicked 0 times</p>
。 - 还记得完成后运行此效果:
() => { document.title = 'You clicked 0 times' }
。
- 反应:当然可以。 更新用户界面。 嘿,浏览器,我要向DOM添加一些东西。
- 浏览器:很酷,我把它画在屏幕上。
- React:好的,现在我要运行您给我的效果。
- 运行
() => { document.title = 'You clicked 0 times' }
。
它更具声明性,不是吗?
在结束时
React Hooks使我们摆脱了一些问题并简化了开发。 我们只需要改变我们的思维模式。 功能组件实际上是道具的UI功能。 他们描述了所有瞬间的一切,并帮助我们忘记了变化。
好吧,我们需要学习如何使用它,但是,嘿,您是第一次正确编写类组件吗?