面向初学者的React自定义Hook问题:
自定义Hook是React 16.8版本中引入的新特性,让你在不编写class的情况下使用state以及其他的React特性。
自定义Hook是一种自定义函数,它封装了React Hook的逻辑,并通过命名约定来使其可重用。自定义Hook可以用来解决组件之间共享的逻辑,例如处理表单的输入、状态管理、异步请求等。使用自定义Hook可以提高代码重用性和可读性,并让代码更易于测试和维护。自定义Hook的命名约定通常以"use"开头,例如"useForm"、"useFetch"等。
通过自定义Hook,可以让你将组件之间的共享状态逻辑提取到可重用的函数中。它们不需要更改组件层次结构,而且不需要引入更深层次的嵌套组件来传递 props。这样可以使代码更加简洁和易于维护。
React 自定义 Hook 的基本规则包括:
Hook 的名称必须以 "use" 开头,以示其为一个 Hook。
Hook 内部只能调用其他的 Hook,不能调用其他的普通函数。
Hook 必须在函数的最顶层使用,不能在循环、条件语句或者子函数中使用。
自定义 Hook 应该具有清晰的功能,而且要足够通用,方便在多个组件中使用。
由于自定义 Hook 本身就是一个函数,因此可以接受参数,也可以返回值。
自定义 Hook 中可以使用 useState、useEffect、useContext 等基础 Hook,也可以使用其他自定义 Hook。
自定义 Hook 一般应该是纯函数,不应该产生副作用。但如果需要产生副作用,可以使用 useLayoutEffect 或 useReducer 等 Hook 来实现。
创建和使用自定义Hook的步骤大致如下:
React 允许开发者通过使用自定义 Hook 来重用组件逻辑。自定义 Hook 是一种 JavaScript 函数,其名称以 “use
” 开头,函数内部可以调用其他的 Hook。
自定义 Hook 可以用来解决以下问题:
import { useState, useEffect } from 'react';
const useFetchData = (url) => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
} catch (error) {
setError(error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [url]);
return { data, isLoading, error };
};
export default useFetchData;
这个自定义 Hook 是用来获取数据的。通过使用 useState
和 useEffect
Hook,我们可以获取到数据、设置加载状态和处理错误。最后通过 return
语句返回数据、加载状态和错误信息。
这个自定义 Hook 可以在组件中使用,如下所示:
import useFetchData from './useFetchData';
const App = () => {
const { data, isLoading, error } = useFetchData('https://jsonplaceholder.typicode.com/todos/1');
if (isLoading) {
return Loading...;
}
if (error) {
return Error: {error.message};
}
return (
{data.title}
{data.body}
);
};
export default App;
在这个组件中,我们通过调用 useFetchData
自定义 Hook 获取数据。如果数据正在加载中,则显示 "Loading...";如果发生错误,则显示错误消息。最后,如果数据获取成功,则显示标题和正文。
import {useEffect, useState} from "react";
export const useWindowSize = () => {
const [width, setWidth] = useState()
const [height, setHeight] = useState()
useEffect(() => {
const {clientWidth, clientHeight} = document.documentElement
setWidth(clientWidth)
setHeight(clientHeight)
}, [])
useEffect(() => {
const handleWindowSize = () =>{
const {clientWidth, clientHeight} = document.documentElement
setWidth(clientWidth)
setHeight(clientHeight)
};
window.addEventListener('resize', handleWindowSize, false)
return () => {
window.removeEventListener('resize',handleWindowSize, false)
}
})
return [width, height]
}
使用:
const [width, height] = useWindowSize()
Effect 是一个 “逃生出口”:当你需要“走出 React 之外”或者当你的使用场景没有更好的内置解决方案时,你可以使用它们。如果你发现自己经常需要手动编写 Effect,那么这通常表明你需要为组件所依赖的通用行为提取一些 自定义 Hook。
例如,这个 useChatRoom
自定义 Hook 把 Effect 的逻辑“隐藏”在一个更具声明性的 API 之后:
function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}
然后你可以像这样从任何组件使用它:
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...
在 React 生态系统中,还有许多用于各种用途的优秀的自定义 Hook。
React 自定义 Hook 和普通 JavaScript 函数有以下不同:
1. 命名规则:React 自定义 Hook 的名称必须以 "use" 开头,这是为了让 React 在编译时能够正确识别和处理它们。
2. 使用规则:React 自定义 Hook 只能在函数组件或其他自定义 Hook 中使用,而普通 JavaScript 函数可以在任何地方使用。React 自定义 Hook 可以使用 React 内置的 Hook,比如 useState、useEffect 等,而普通 JavaScript 函数则不能使用这些 Hook。
3. 使用场景:React 自定义 Hook 主要用于将组件中共用的逻辑抽离出来,方便复用和维护,而普通 JavaScript 函数则可以处理任何数据和业务逻辑。
4. 内部实现:自定义Hook可以访问React的state和其他的React特性,而普通的JavaScript函数则不能。此外,每次在组件中调用自定义Hook,它都会获取独立的state。即使两个组件使用了相同的Hook,它们也不会共享state。这是因为自定义Hook是一种重用状态逻辑的机制(例如设置为订阅并存储当前值),所以每次使用自定义Hook时,其中的所有state和副作用都是完全隔离的。
总之,React 自定义 Hook 是 React 特有的一种函数,用于处理组件共用的逻辑,与普通 JavaScript 函数有些许不同。
useEffect和useLayoutEffect是React中的两个用于处理副作用(side effects)的函数。useEffect在全部渲染完毕后才会执行,而useLayoutEffect则会在浏览器layout之后,painting(绘制)之前执行。它们与自定义Hook的关系在于,你可以在自定义Hook中使用这些函数来处理副作用,例如在useEffect中订阅事件并在useLayoutEffect中获取布局信息。
在处理副作用时,可以将副作用封装到一个单独的函数中,然后在需要的地方调用这个函数。这样做可以确保副作用不会影响到其他的代码逻辑。同时,如果在一个组件中使用多个自定义Hook,每个Hook的副作用都是独立的,不会互相影响。