React Hooks -- useRef/useContext/useMemo/useCallback/自定义Hooks
<p> <strong>useRef</strong></p><p> useRef()返回一个具有current属性的对象,称为ref对象。把对象赋值给原生的React Element元素的ref属性,就能获取到对应的真实的DOM元素。</p>
<div class="cnblogs_code">
<pre>import React, { useRef } from "react"<span style="color: rgba(0, 0, 0, 1)">;
const CustomTextInput </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const textInput </span>= useRef(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建ref对象</span>
const focusTextInput = () => textInput.current.focus(); <span style="color: rgba(0, 128, 0, 1)">// 组件挂载完成,</span><span style="color: rgba(0, 128, 0, 1)">ref对象的current属性指向真实的DOM(input元素),调用focus方法</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> (<>
<input type="text" ref={textInput} />
<button onClick={focusTextInput}>Focus the text input</button>
</>);
}</pre>
</div>
<p> 把ref对象赋值给组件类型的React Element的ref属性时,也能获取到子组件,调用子组件中的方法,不过要配合使用forwardref和useImperativeHandle。forwardref把函数子组件包起来,组件就多接收了一个ref参数,把ref和要暴露出来的方法传递给useImperativeHandle。使用create-react-app 创建React项目,在src中创建一个Counter组件</p>
<div class="cnblogs_code">
<pre>import React, { forwardRef, useImperativeHandle } from 'react'<span style="color: rgba(0, 0, 0, 1)">;
import { useState } from </span>"react"
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 组件被forwardRef之后,组件多接受一个ref参数</span>
const Counter = (props, ref) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const clickHandler </span>= () => {setCount(c => c + 1<span style="color: rgba(0, 0, 0, 1)">);}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 第一个参数就是ref,第二个参数是函数,返回一个对象,父组件中ref对象的current属性就指向这个对象。</span>
useImperativeHandle(ref, () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ({
click: clickHandler
})
})<br>
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <p>count is {count} </p>
<span style="color: rgba(0, 0, 0, 1)">}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> forwardRef(Counter); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> export forwardRef(组件)</span></pre>
</div>
<p> App中使用ref,获取的子组件暴露出来的对象,并在button的click回调函数中使用</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
const counteRef </span>=<span style="color: rgba(0, 0, 0, 1)"> useRef();
const handleClick </span>= () => {counteRef.current.click();} <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ref对象获取到子组件暴露出来的对象。</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> (<React.Fragment>
<Counter ref={counteRef}></Counter> //<span style="color: rgba(0, 0, 0, 1)"> ref对象赋值给子组件的ref属性。
</span><button onClick={handleClick}>Add</button>
</React.Fragment>);
}</pre>
</div>
<p> 如果项目中使用了Redux和React-Redux,connect中配置forwardRef: true </p>
<div class="cnblogs_code">
<pre>connect(null, null, null, { forwardRef: true })(组件);</pre>
</div>
<p> ref对象是一个对象,属性可以保存任何值,useRef()可以接受一个参数,给属性赋初始值。有一个值在组件中,但它又与组件渲染无关,不是状态,不是属性,也不在JSX中使用,比如setTimeout返回的ID,这种值就可以放到ref中。更改ref的值,不会引起组件的重新渲染,因为值与渲染无关,并且在组件的整个生命周期中,ref对象一直存在。组件挂载,ref对象创建,组件销毁,ref对象销毁。</p>
<div class="cnblogs_code">
<pre>import React, { useRef, useEffect } from "react"<span style="color: rgba(0, 0, 0, 1)">;
const Timer </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const intervalRef </span>=<span style="color: rgba(0, 0, 0, 1)"> useRef();
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
const id </span>= setInterval(() => {console.log("A second has passed");}, 1000<span style="color: rgba(0, 0, 0, 1)">);
intervalRef.current </span>=<span style="color: rgba(0, 0, 0, 1)"> id;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> () =><span style="color: rgba(0, 0, 0, 1)"> clearInterval(intervalRef.current);
});
const handleCancel </span>= () =><span style="color: rgba(0, 0, 0, 1)"> clearInterval(intervalRef.current);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">...</>);</span>
}</pre>
</div>
<p> 需要注意的是更新ref对象的值,是一个副作用,因为它脱离React渲染过程,所以要放到useEffect或useLayoutEffect中,或放到事件处理函数中。</p>
<div class="cnblogs_code">
<pre>import React, { useRef } from "react"<span style="color: rgba(0, 0, 0, 1)">;
const RenderCounter </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
const counter </span>= useRef(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> counter.current = counter.current + 1; 不建议直接写在这里</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 建议放到useEffect中</span>
useEffect(() =><span style="color: rgba(0, 0, 0, 1)"> {
counter.current </span>= counter.current + 1<span style="color: rgba(0, 0, 0, 1)">;
});
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><h1>{`The component has been re-rendered ${counter} times`}</h1>
<span style="color: rgba(0, 0, 0, 1)">);
};</span></pre>
</div>
<p> <strong>useContext</strong></p>
<p> React Context使组件之间可以共享数据,但不用从父组件层层传递到子组件。Context提供了一个上下文(环境),只要在这个上下文中,子组件可以直接获取。Context.Provider包含起来的组件,就为组件提供了上下文,它的value(共享数据)就是可供子组件直接获取的数据(使用useContext获取)。React.createContext()创建一个context,它接受一个可选的参数,就是共享数据的默认值。比如登录之后,用户信息,页面的其他地方都要获取到,把用户信息放到Context中。create-react-app react-context 创建项目,userContext.js 创建context对象</p>
<div class="cnblogs_code">
<pre>import React from 'react'<span style="color: rgba(0, 0, 0, 1)">;
export const UserContext </span>=<span style="color: rgba(0, 0, 0, 1)"> React.createContext(</span><span style="color: rgba(0, 0, 0, 1)">)</span></pre>
</div>
<p> App.js 中,Header组件用于获取用户信息,Detail用于显示信息,要设一个user状态和改变user的setUser,让这两个数据共享,所以把它们用Context包起来。</p>
<div class="cnblogs_code">
<pre>import React, {useState} from "react"<span style="color: rgba(0, 0, 0, 1)">;
import Header from </span>"./Header"<span style="color: rgba(0, 0, 0, 1)">;
import Details from </span>'./Details'<span style="color: rgba(0, 0, 0, 1)">;
import { UserContext } from </span>'./userContext'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useState({});
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><UserContext.Provider value={{ user, setUser }}>
<Header></Header>
<Details></Details>
</UserContext.Provider>
<span style="color: rgba(0, 0, 0, 1)"> )
}</span></pre>
</div>
<p> Header和Detail都消费context中的数据,</p>
<div class="cnblogs_code">
<pre>import React, { useEffect, useContext } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import { UserContext } from </span>'./userContext'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Header() {
const { user, setUser } </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(UserContext);
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
fetch(</span>'https://jsonplaceholder.typicode.com/users/1'<span style="color: rgba(0, 0, 0, 1)">)
.then(response </span>=><span style="color: rgba(0, 0, 0, 1)"> response.json())
.then(user </span>=><span style="color: rgba(0, 0, 0, 1)"> setUser(user))
})
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <div>用户名:{user.name}</div>
<span style="color: rgba(0, 0, 0, 1)">}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Details() {
const { user } </span>=<span style="color: rgba(0, 0, 0, 1)"> useContext(UserContext);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> (<div>详细信息: {`${user.email} ${user.phone}`}</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}</span></pre>
</div>
<p> 这有一个问题,Header中setUser时,App所有子组件都会重新渲染,能不能只渲染consumer组件?要使用children属性,children是一个特殊的属性,<A><B/> </A>,A组件包起来的B组件是A的children属性,它和A组件是同一个父组件。A组件怎么发生变化,B组件都不会变化。实现一个A组件,接受children属性,把要获取数据的子组件作为children传递给它。UserContext.js 添加</p>
<div class="cnblogs_code">
<pre>export const UserProvider = ({ children }) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(''<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <UserContext.Provider value={{ user, setUser }}><span style="color: rgba(0, 0, 0, 1)">
{children}
</span></UserContext.Provider>
}</pre>
</div>
<p> App.js</p>
<div class="cnblogs_code">
<pre>import { UserProvider } from './userContext'<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> App() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><UserProvider>
<Header></Header>
<Details></Details>
</UserProvider>
<span style="color: rgba(0, 0, 0, 1)"> )
}</span></pre>
</div>
<p> 在Header中调用setUser时,UserProvider组件会重新渲染,但UserProvider的children不会重新渲染,因为children是从props中获取的属性,没有改变。只有消费context的组件(children)才会重新渲染。context的consumer,当Provider的值改变时,consumer 重新渲染。Any components that consume the context, however, do re-render in response to the change of value on the provider, not because the whole tree of components has re-rendered.</p>
<p> 随着共享数据越来越多,theme, user等,如果都写到一个value,如果value中有一个值改变,所有consumer都会重新渲染,没有必要。可以使用多个provider</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> AppProvider({ children }) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可能存在状态</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><ThemeContext.Provider value="lava">
<UserContext.Provider value="sam">
<LanguageContext.Provider value="en"><span style="color: rgba(0, 0, 0, 1)">
{children}
</span></LanguageContext.Provider>
</UserContext.Provider>
</ThemeContext.Provider >
<span style="color: rgba(0, 0, 0, 1)"> );
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用它</span>
<AppProvider>
<App/>
</AppProvider></pre>
</div>
<p> 子组件,可以只想获取自己想要的值。</p>
<div class="cnblogs_code">
<pre>function InfoPage(props) {
const theme = useContext(ThemeContext);
const language = useContext(LanguageContext);
return (/* UI */);
}
function Messages(props) {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
// subscribe to messages for user
return (/* UI */);
}</pre>
</div>
<p> 组件之间简单共享数据另一种方式是Render props:就是属性可以被渲染(render)。属性可以是组件了,把整个组件作为属性进行传递,或者,属性是个函数,返回一个被渲染的组件。当属性是函数时,可以传一个data,渲染返回的组件时使用。比如Button组件有一个图标,为了能够更改图标,可能要提供size,width,color等参数,Button组件接受的参数越来越多,Button和Icon强绑定到一起,为什么不让图标整个组件传递过来?Button不管什么图标,给了,就渲染到合适的位置就行。</p>
<div class="cnblogs_code">
<pre>export function List({ data =<span> [], renderItem, renderEmpty }) {
return data.length === 0
?<span> (renderEmpty)
: (
<ul><span>
{data.map((item, i) =><span> (
<li key={i}>{renderItem(item)}</li><span>
))}
</ul><span>
);
}</span></span></span></span></span></span></pre>
</div>
<p> 使用组件</p>
<div class="cnblogs_code">
<pre><<span>List
data={[{name: 'Rose', elevation: 2<span>}]}
renderEmpty={<p>This list is empty</p><span>}
renderItem={item =><span> <span>{item.name} -<span> {item.elevation.toLocaleString()}ft<span>}
/></span></span></span></span></span></span></span></pre>
</div>
<p> <strong>useMemo/useCallback</strong></p>
<p> React函数组件的最大问题就是,每次更新,组件就会重新渲染。里面的值就会重新计算,函数就会重新创建。函数通常会作为参数传递给子组件,又会引起子组件的重新渲染,导致没有必要的渲染。如果值不想重新计算,就要useMemo,记住函数的返回值。函数不想重新创建,就用useCallback包起来,它不会生成新函数,返回原函数。当然,它们都接受第二个参数,依赖数组,指定它们接受的函数的依赖,如果依赖有变化,值会重新计算并记住,函数会重新生成。子组件再配合使用React.memo,组件被调用的时候有相同的属性,子组件不会重复渲染。React.memo也可以单独使用,当一个组件渲染的时候,它的子组件也会渲染,不管子组件有没有变化,使用React.memo,子组件不会重新渲染。当把对象或数组作属性传递给子组件时,传递过去的是引用,所以才要求setState返回一个全新的对象,要不然传递过去的都是同一个引用,子组件不会更新。</p>
<div class="cnblogs_code">
<pre>import { useState } from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Items({ items }) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<h2>Todo items</h2>
<ul><span style="color: rgba(0, 0, 0, 1)">
{items.map((todo) </span>=> <li key={todo}>{todo}</li>)}
</ul>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Todo() {
const </span>= useState(["Clean gutter", "Do dishes"<span style="color: rgba(0, 0, 0, 1)">]);
const </span>= useState(""<span style="color: rgba(0, 0, 0, 1)">);
const onSubmit </span>= (evt) =><span style="color: rgba(0, 0, 0, 1)"> {
setItems((items) </span>=><span style="color: rgba(0, 0, 0, 1)"> items.concat());
setNewItem(</span>""<span style="color: rgba(0, 0, 0, 1)">);
evt.preventDefault();
};
const onChange </span>= (evt) =><span style="color: rgba(0, 0, 0, 1)"> setNewItem(evt.target.value);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><main>
<Items items={items} />
<form onSubmit={onSubmit}>
<input value={newItem} onChange={onChange} />
<button>Add</button>
</form>
</main>
<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span> App() { <span style="color: rgba(0, 0, 255, 1)">return</span> <Todo />;}
export <span style="color: rgba(0, 0, 255, 1)">default</span> App;</pre>
</div>
<p> 对于Items组件来说,items属性是引用。在输入框中输入数据,Items组件也会被渲染,只是因为父组件渲染,子组件一定会渲染,而不是因为items属性发生变化,此时Items引用并没有发生变化。只有点击Add按钮,setState返回了一个全新的数组,items属性指向了另外一个引用,这时才是因为属性发生了变化,子组件会重新渲染。输入时,items子组件是没有必要重复渲染的,所以可以用React.memo 包起来。但是当在Jsx中内联一个数组时,<Items items={['Complete to do list', ...items]},输入数据,父组件每次渲染都会重新创建一个数组,React.momeo 就不起作用了,这时用useMemo。</p>
<div class="cnblogs_code">
<pre><span>function Todo() {
// ...
const allItems = useMemo(() => ["Complete todo list"<span>], )
return<span> (
<main>
<Items items={allItems} />
// ...
</main><span>
);
}</span></span></span></span></pre>
</div>
<p> In fact, useMemo doesn't do anything more than an assignment, except the assignment is conditional。如果Items 组件要删除,它需要接受函数参数,那就要把函数memorize.</p>
<div class="cnblogs_code">
<pre>const onDelete = useCallback((item) =><span> {
setItems((list) => list.filter(i => i !==<span> item))
}, [])
<Items items={allItems} onDelete ={onDelete} /></span></span></pre>
</div>
<p> useMemo和useCallback只是返回一个值或函数,它和React的更新机制没有关系。</p>
<div class="cnblogs_code">
<pre>const Title = () =><span style="color: rgba(0, 0, 0, 1)"> {
const a </span>= useMemo(() =><span style="color: rgba(0, 0, 0, 1)"> { ... }, [])
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <Child a={a} />
}</pre>
</div>
<p> Title组件更新,Child组件一定更新,不管a的值有没有变化。</p>
<p> <strong>自定义hooks</strong></p>
<p> 使用hooks的函数组件鼓励你把相关副作用的逻辑放到一个地方。如果副作用是一个许多组件都需要的功能,可以把这段副作用的代码逻辑抽取出来,形成一个函数,这个函数就是自定义hook。比如做调查问卷的问题组件,都要加载问题,用户都要订阅,可以他们抽取到自定义hook中。和这些副作用相关的state,也要同步移动到相应的hook 中(Any state that is used solely for those 副作用 can be moved itno the corresponding hook)。</p>
<p><img src="https://img2024.cnblogs.com/blog/1013082/202409/1013082-20240903220030812-991005260.png"></p>
<p> 自定义hook可以维护自己的状态,它需要state来完成它的功能。由于hook 仅仅是函数,如果其他组件需要访问任意hook的state,hook可以把state 进行返回,包含到它的返回值中。比如一个自定义hook根据userId获取用户信息,它可以把获取到的用户数据store it locally,然后把它返回给调用hook的组件。每一个hook 封装自己的状态,就像其他函数一样。</p>
<div class="cnblogs_code">
<pre>const useToggle = (initialStatus = <span style="color: rgba(0, 0, 255, 1)">false</span>) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useState(initialStatus)
const toggle </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
setStatus(status </span>=> !<span style="color: rgba(0, 0, 0, 1)">status)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p> 简单自定义fetch hook</p>
<div class="cnblogs_code">
<pre>import React, { useState, useEffect } from "react"<span style="color: rgba(0, 0, 0, 1)">;
export </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useFetch(uri){
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useState();
const </span>=<span style="color: rgba(0, 0, 0, 1)"> useState();
const </span>= useState(<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!uri) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
fetch(uri).then(data </span>=><span style="color: rgba(0, 0, 0, 1)"> data.json())
.then(setData)
.then(() </span>=> setLoading(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">))
.</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)">(setError);
}, );
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> { loading, data, error };
}</span></pre>
</div>
<p> 在useFetch的基础上,如果一个应用中,loading和error的处理方式都一样,完全可以封装出一个fetch组件出来。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Fetch({ url, renderSucess,
loadingFallback </span>= <p>loading</p>,
renderError = error => {<pre>{JSON.stringify(error)}</pre> }
<span style="color: rgba(0, 0, 0, 1)">}) {
const {loading, data, error} </span>=<span style="color: rgba(0, 0, 0, 1)"> useFetch(url);
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(loading) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> loadingFallback;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (error) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> renderError(error);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (data) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> renderSucess({data});
}</span></pre>
</div>
<p> 自定义hooks,当前组件是否已经挂载</p>
<div class="cnblogs_code">
<pre>export <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> useMountedRef() {
const mounted </span>= useRef(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
useEffect(() </span>=><span style="color: rgba(0, 0, 0, 1)"> {
mounted.current </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> () => (mounted.current = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mounted;
}</span></pre>
</div>
<p> 当使用自定义hooks时,hooks内状态发生变化,使用hooks的组件会重新渲染(if the hook's state changes, the "host" component will re-render 相当于<strong>lifted state up</strong>),使用自定义hooks的组件一定小,只重复渲染它自己(把自定义hooks包装到小的组件中)。当在一个自定义hooks中,使用另一个hooks,hooks中状态变化会冒泡到包含最外层自定义hooks的组件。chaining hooks: if a hook’s state changes, it will cause its “host” hook change as well, which will propagate up through the whole chain of hooks until it reaches the “host” component and re-renders it (which will cause another chain reaction of re-renders, only downstream now)。一个自定义hooks,一个state,不相干的state放到不同的自定义hooks中,一个自定义hooks做一件事。当有了自定义hooks,useEffect的执行顺序是子组件的useEffect先执行,父组件的自定义hooks中的useEffect后执行,最后才是父组件本身的useEffect执行。</p>
<p> </p>
<p> 自定义hooks也是函数,只不过使用useState和useEffect等基本hook,把带有状态的公共逻辑抽离出来,一个自定义hooks可以使用另外一个自定义hooks,react的更新是组件级更新,最小的更新的单元是组件,只要一个hooks引起状态改变,最顶层hooks的所在组件,就会更新,导致所有子组件更新。</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/SamWeb/p/15976977.html
頁:
[1]