irpas技术客

react hook 详解及使用_苦不尽甘常在-慎独_reacthook

大大的周 6458

1、hooks 是什么

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

2、为什么要使用 hooks

难以理解的 class 、组件中必须去理解 javascript 与 this 的工作方式、需要绑定事件处理器、纯函数组件与 class 组件的差异存在分歧、甚至需要区分两种组件的使用场景;Hook 可以使你在非 class 的情况下可以使用更多的 React 特性。

在组件之间复用状态逻辑很难、大型组件很难拆分和重构,也很难测试。Hook 使你在无需修改组件结构的情况下复用状态逻辑。

复杂组件变得难以理解、业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)

3、react 中我们常用的 hook 什么是 Hook?

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

下面我们挑几种介绍一下(useState、userContext、useEffect )

1) useState 状态钩子

import React, { useState } from 'react'; function Example() { // 声明一个叫 "count" 的 state 变量 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> </div> ); }

等价的 class 示例

class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button> </div> ); } }

什么时候我会用 Hook? 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。

在 useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值。类似 this.state,第二项是一个函数,用来更新状态,类似 setState。

上述例子中没有 class 继承、没有 this、没有生命周期、代码更加简洁、这就是使用 hooks 的意义;

2)useContext() 共享状态钩子

如果需要在深层次组件之间共享状态,可以使用 useContext()。context 提供了一种在组件之间共享 props 的方式,而不必显示地通过组件树的逐层传递 props; useContext 钩子比原始 class 组件中使用 context 更为方便;

假设现在有俩个组件 Navbar 和 Messages,我们希望它们之间共享状态。

<div className="test"> <Navbar /> <Messages /> </div>

使用方法如下: 第一步在它们的父组件上使用 React 的 Context API,在组件外部建立一个 Context。

import React, { useContext } from 'react'; const TestContext = React.createContext({}); const Navbar = () => { const { username } = useContext(TestContext); return ( <div className="navbar"> <p>{username}</p> </div> ); }; const Messages = () => { const { messageDetail } = useContext(TestContext); return ( <div className="messages"> <p>1 message for {messageDetail}</p> </div> ); }; const App = () => { return ( <TestContext.Provider value={{ username: 'superawesome', messageDetail: 'hello world', }} > <div className="test"> <Navbar /> <Messages /> </div> </TestContext.Provider> ); }; export default App;

等价的 class 用例

import React from 'react'; const TestContext = React.createContext({}); const Navbar = () => ( <TestContext.Consumer> {({ username }) => ( <div className="messages"> <p>1 message for {username}</p> </div> )} </TestContext.Consumer> ); const Messages = () => { return ( <TestContext.Consumer> {({ messageDetail }) => ( <div className="messages"> <p>1 message for {messageDetail}</p> </div> )} </TestContext.Consumer> ); }; class App extends React.Component { render() { return ( <TestContext.Provider value={{ username: 'superawesome', messageDetail: 'hello world', }} > <div className="test"> <Navbar /> <Messages /> </div> </TestContext.Provider> ); } } export default App;

两种用法相比较 hooks 更加简介易懂、render 中也没有函数式写法!

3)useEffect 副作用钩子

它可以用来更好的处理副作用,如异步请求等;可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

useEffect(() => {}, [array]);

使用 useEffect 更新网页 title

import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; },[count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> </div> ); }

使用 class (等价)

class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button> </div> ); } }

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出 Effect 的依赖项。只要这个数组发生变化,useEffect()就会执行。当第二项省略不填时,useEffect()会在每次组件渲染时执行。这一点类似于类组件的 componentDidMount。

我们实现一个 useEffect() 依赖项变化的例子

import React, { useState, useEffect } from 'react'; const AsyncCount = ({ countNum }) => { const [loading, setLoading] = useState(true); const [count, setCount] = useState(0); useEffect(() => { setLoading(true); setTimeout(() => { setLoading(false); setCount(countNum); }, 2000); }, [countNum]); return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>; }; const TestCount = () => { const [count, setCount] = useState(0); const changeCount = (name) => { setCount(name); }; return ( <> <AsyncCount countNum={count} /> <button onClick={() => { changeCount(count + 1); }} > 增加 </button> <button onClick={() => { changeCount(count - 1); }} > 减少 </button> </> ); }; export default TestCount;

再上述例子中,我们把处理 count 异步的操作以及是否渲染 loading,都放在了 AsyncCount hook 中;把复杂操作,通过 hooks 提取出去;将组件中关联部分拆分;

那下面我们在做一个更加细化的拆分,拆出一个自己的 hook import React, { useState, useEffect } from 'react'; const useCount = (countNum) => { const [loading, setLoading] = useState(true); const [count, setCount] = useState(0); useEffect(() => { setLoading(true); setTimeout(() => { setLoading(false); setCount(countNum); }, 2000); }, [countNum]); return [loading, count]; }; const AsyncCount = ({ countNum }) => { const [loading, count] = useCount(countNum); return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>; }; const TestCount = () => { const [count, setCount] = useState(0); const changeCount = (count) => { setCount(count); }; return ( <> <AsyncCount countNum={count} /> <button onClick={() => { changeCount(count + 1); }} > 增加 </button> <button onClick={() => { changeCount(count - 1); }} > 减少 </button> </> ); }; export default TestCount;

我们在上述 AsyncCount 组件中再次将它的副作用操作拆分;在此组件中只关注渲染结果,useCount 接受一个数字,返回一个数组,数组中包括状态,与 count 两个结果;在我们使用 useCount 时,会根据我们传入参数的不同而返回不同的状态;

另外关于 hook 其他注意事项、在使用 useState 和一些其他钩子时、它们彼此的对应关系、是依靠 Hook 调用的顺序、所以我们不要在循环、条件、或嵌套函数中使用 hook、确保总是在你的 React 函数的最顶层调用他们,这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确;

用代码来解释一下顶层的意思

if (count !== 0) { useEffect(function persistForm() { ... }); }

这样会导致在 count 不等于 0 时,条件值为 true,会执行此次 hook,但如果在某次渲染时、如 count 值变为 0,那么此次 hook 将不在渲染,从而导致 hook 顺序发生了改变,产生不可预知的 bug;

其次在 hooks 中在 setState 值为 Object 或 Array 时,由于为引用类型,setState 通过回调函数的形式赋值,其参数值存的是 obj 的地址,由于 react 中 state 是只读的,所以在操作本身值改变是无法成功的,所以我们应当拷贝出去一份,在重新赋值;还有 hooks 异步获取当前 state 值问题等等,遇到问题多百度;

它为我们本来的开发带来了很大的变化、更加便于我们操作使用;come on!


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #reacthook #1hooks #是什么Hook # #React #168 #的新增特性 #它可以让你在不编写