大白话React Hooks,新特性怎么用?
啥是 React Hooks
React Hooks 是 React 16.8 版本引入的新特性,它能让你在不编写 class
的情况下使用 state
以及其他的 React 特性。就好比以前你盖房子得用一种特定的建筑方式(class
组件),现在有了新工具(Hooks),不用那种特定方式也能盖出很棒的房子。它可以让你的代码更简洁、更易复用。
常用的 React Hooks 及其使用方法
1. useState
- 作用:让你在函数组件里使用
state
,就像给函数组件装上了能存储数据的小盒子。 - 使用方法:它接收一个初始值作为参数,返回一个数组,数组的第一个元素是当前的
state
值,第二个元素是一个函数,用于更新这个state
。
import React, { useState } from 'react';
function Counter() {
// 定义一个名为 count 的 state,初始值为 0
// useState 返回一个数组,第一个元素是 count 的值,第二个元素是更新 count 的函数 setCount
const [count, setCount] = useState(0);
return (
<div>
{/* 显示 count 的值 */}
<p>你点击了 {count} 次</p>
{/* 点击按钮时调用 setCount 函数,将 count 的值加 1 */}
<button onClick={() => setCount(count + 1)}>
点击我
</button>
</div>
);
}
export default Counter;
2. useEffect
- 作用:可以让你在函数组件里执行副作用操作,比如数据获取、订阅、手动修改 DOM 等。就像在房子盖好后,你可以用它来做一些额外的装饰或者布置。
- 使用方法:它接收一个回调函数作为第一个参数,这个回调函数会在组件渲染后执行。第二个参数是一个可选的数组,用于指定哪些值发生变化时才执行这个回调函数。如果不传第二个参数,回调函数会在每次组件渲染后都执行;如果传一个空数组,回调函数只会在组件第一次渲染后执行。
import React, { useState, useEffect } from 'react';
function DataFetcher() {
// 定义一个名为 data 的 state,初始值为 null
const [data, setData] = useState(null);
// 使用 useEffect 进行数据获取
useEffect(() => {
// 模拟一个异步的数据获取操作
const fetchData = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const result = await response.json();
// 将获取到的数据更新到 data state 中
setData(result);
};
// 调用数据获取函数
fetchData();
// 可以返回一个清理函数,用于在组件卸载时执行一些清理操作,比如取消订阅等
return () => {
console.log('组件卸载了');
};
}, []); // 传入空数组,表示这个 useEffect 只在组件第一次渲染后执行
return (
<div>
{data? (
// 如果 data 不为 null,显示数据的标题
<p>{data.title}</p>
) : (
// 如果 data 为 null,显示加载中
<p>加载中...</p>
)}
</div>
);
}
export default DataFetcher;
3. useContext
- 作用:让你在函数组件里使用 React 的上下文(Context),可以方便地在组件树中传递数据,而不用一层一层地通过
props
传递。就像有一个公共的仓库,所有组件都能去里面拿东西。 - 使用方法:它接收一个
Context
对象作为参数,返回这个Context
当前的值。
import React, { createContext, useContext } from 'react';
// 创建一个 Context 对象,初始值为 '默认值'
const MyContext = createContext('默认值');
function ChildComponent() {
// 使用 useContext 获取 MyContext 的值
const value = useContext(MyContext);
return (
<div>
{/* 显示从上下文获取的值 */}
<p>从上下文获取的值: {value}</p>
</div>
);
}
function ParentComponent() {
return (
// 使用 MyContext.Provider 提供一个新的值 '新的值'
<MyContext.Provider value="新的值">
<ChildComponent />
</MyContext.Provider>
);
}
export default ParentComponent;
4. useReducer
- 作用:是
useState
的替代方案,适用于复杂的state
逻辑,比如多个子值或者下一个state
依赖于之前的state
。就像一个更强大的state
管理工具。 - 使用方法:它接收一个
reducer
函数和一个初始state
作为参数,返回一个数组,数组的第一个元素是当前的state
值,第二个元素是一个dispatch
函数,用于触发reducer
中的操作。
import React, { useReducer } from 'react';
// 定义一个 reducer 函数,接收当前 state 和一个 action,返回新的 state
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};
function CounterWithReducer() {
// 使用 useReducer 定义 state 和 dispatch 函数
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
{/* 显示 count 的值 */}
<p>你点击了 {state.count} 次</p>
{/* 点击按钮时调用 dispatch 函数,触发 increment 操作 */}
<button onClick={() => dispatch({ type: 'increment' })}>
增加
</button>
{/* 点击按钮时调用 dispatch 函数,触发 decrement 操作 */}
<button onClick={() => dispatch({ type: 'decrement' })}>
减少
</button>
</div>
);
}
export default CounterWithReducer;
通过这些 Hooks,你可以在函数组件里实现很多原本 class
组件才能实现的功能,让代码更加简洁和易于维护。
如何优化使用React Hooks的代码性能?
咱来唠唠怎么优化使用 React Hooks 的代码性能,下面从几个方面详细说说:
别让组件瞎渲染
- 给组件加个“检查器”:
React.memo
就像是个检查官,能帮函数组件检查传入的props
有没有变化。要是props
和上次一样,它就不让组件重新渲染,省了不少力气。比如说你有个组件专门显示用户信息,只要用户信息的props
没改,这个组件就不用重新画一遍,这样性能就提升了。代码大概这么写:
import React from'react';
// 用 React.memo 包裹组件
const UserInfo = React.memo(({ name, age }) => {
return (
<div>
<p>姓名: {name}</p>
<p>年龄: {age}</p>
</div>
);
});
export default UserInfo;
- 缓存计算结果:
useMemo
就像个小仓库,能把一些计算结果存起来。要是依赖的东西没变化,就直接从仓库里拿结果,不用重新算一遍。比如你有个组件要对一个数组里的数字做复杂计算,每次数组没变就不用重新算,直接用之前算好的结果就行。代码示例如下:
import React, { useMemo } from'react';
function CalculateList({ numbers }) {
const calculatedResult = useMemo(() => {
// 假设这里是复杂的计算逻辑
return numbers.map(num => num * 2);
}, [numbers]);
return (
<ul>
{calculatedResult.map(result => (
<li key={result}>{result}</li>
))}
</ul>
);
}
export default CalculateList;
用好 useEffect
- 精准设置依赖项:
useEffect
就像个小跟班,会在组件渲染后做一些额外的事儿。但你得告诉它什么时候该干活,这就靠依赖项数组。要是依赖项数组是空的,它就只在组件刚创建和销毁的时候干活;要是数组里有东西,这些东西变了它才会重新干活。比如说你有个组件要在count
变化时发个网络请求,那就把count
放进依赖项数组里。代码如下:
import React, { useState, useEffect } from'react';
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// 只有 count 变化时才会发请求
console.log(`Count 变成了 ${count}`);
// 这里可以放网络请求的代码
}, [count]);
return (
<div>
<button onClick={() => setCount(count + 1)}>加 1</button>
</div>
);
}
export default MyComponent;
- 别在 useEffect 里瞎更新:要是在
useEffect
里做了会让组件重新渲染的事儿,得确保这事儿是必要的,不然就会陷入无限循环,像个疯了的小陀螺。你可以用useRef
来保存一些临时状态,避免不必要的更新。
优化事件处理
- 缓存事件处理函数:
useCallback
就像个函数小管家,能把事件处理函数存起来。每次组件渲染时,只要依赖项没变,它就还是用原来的函数,不用重新创建一个新的。比如你有个按钮的点击事件处理函数,用useCallback
缓存后,性能会更好。代码如下:
import React, { useState, useCallback } from'react';
function MyButton() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return <button onClick={handleClick}>点击我</button>;
}
export default MyButton;
其他小窍门
- 把大组件拆成小零件:要是一个组件功能太多、太复杂,就把它拆成好几个小的、功能单一的组件。这样每个小组件好维护,也容易复用,性能自然就上去了。
- 别在循环里乱用 Hooks:React 规定 Hooks 只能在组件最外面或者自定义 Hooks 里面用,别在循环、判断或者嵌套函数里用,不然会出性能问题,还不好找错。
- 懒加载组件:有些组件不常用或者加载起来特别慢,就用 React 的懒加载功能。等需要用这个组件的时候再去加载它,这样页面一开始加载得就快多了。