开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
使用React Hook可以帮助你更好的复用状态逻辑,可以让组件中相关联的部分拆成更小的函数,可以在class这个限制外使用更多的React特性。
今天我们就来学习一些常用的React Hook是如何使用的。
useState
在useState出现之前,只有类组件被称为状态组件,而函数组件被称为无状态组件。useState的出现
让函数组件也可以拥有状态。
useState返回两个值,第一个是数据,第二个是改变数据的方法。
import { useState } from "react";
export default function Example() {
// 声明一个叫 "count" 的 state 变量 初始值为 0
const [count, setCount] = useState(0);
// 设置一个person对象 初始值为 {name: "rose"}
const [person, setPerson] = useState({ name: "rose" });
return (
<div>
<p>You clicked {count} times</p>
<p>{person.name}</p>
<p>{person.age}</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={() => setPerson({ ...person, age: "321" })}>
设置age
</button>
</div>
);
}
useEffect
有时候我们想在React更新DOM之后做一些其他的操作。可以通过useEffect方法,传递一个effect方法,该方法在DOM更新中之后调用。effect方法默认在每次DOM更新(第一次渲染和每次更新)之后都会执行。
import { useState, useEffect } from "react";
export default function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`You clicked ${count} times`);
});
return (
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
点击按钮会触发useEffect里的effect方法。
可以在effect方法里面返回一个清除方法,当要执行下一次effect函数的时候会先执行上一次的effect清除方法。
import { useState, useEffect } from "react";
export default function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`1effect`);
return () => {
console.log("destoryed1");
};
});
return (
<div>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
);
}
⬆️点击按钮之后会执行上一次effect方法返回的函数。
给useEffct的传入第二个参数(一个数组),可以根据数组里面数据的变化来执行effect方法。
import { useState, useEffect } from "react";
export default function Example() {
const [count, setCount] = useState(0);
const [name, setName] = useState("🚀");
useEffect(() => {
console.log(`1effect`);
}, [name]); // 只有修改name 会触发effect
return (
<div>
{name}
{count}
{/* 不会触发effect */}
<button onClick={() => setCount(count + 1)}>+1</button>
{/* 会触发effect */}
<button onClick={() => setName("🔩")}>变🔩</button>
</div>
);
}
当给第二个参数传递一个空数组的时候,该effect方法只会在第一次渲染的时候执行。
import { useState, useEffect } from "react";
export default function Example() {
const [count, setCount] = useState(0);
const [name, setName] = useState("🚀");
useEffect(() => {
console.log(`1effect`);
}, []);
return (
<div>
{name}
{count}
{/* {count > 1 ? <Son></Son> : <Daughter></Daughter>} */}
{/* 这两个按钮点击 都不会再触发effect了*/}
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setName("🔩")}>变🔩</button>
</div>
);
}
useLayoutEffect
useEffect是异步执行的,而useLayoutEffect是同步执行的,用法都是一样的。
import { useState, useEffect, useLayoutEffect } from "react";
export default function Example() {
const [count, setCount] = useState(1);
// 会出现闪烁的效果
useEffect(() => {
if (count === 0) {
setCount(10 + Math.random() * 2000);
}
}, [count]);
// 不会出现闪烁的效果
// useLayoutEffect(() => {
// if (count === 0) {
// setCount(10 + Math.random() * 2000);
// }
// }, [count]);
return (
<div>
count:{count}
<button onClick={() => setCount(0)}>置为0</button>
</div>
);
}
useContext
我们之前有学过React.createContext得到的Provicer和Consumer来实现跨级组件的数据传递。useContext则是实现Consumer的React Hooks用法。
import Test from "./Test";
import React from "react";
export const Con = React.createContext(null);
export default function App() {
return (
<div>
<Con.Provider value={{ name: "rose" }}>
<Test></Test>
</Con.Provider>
</div>
);
}
import { useContext } from "react";
import { Con } from "./App";
export default function Test() {
const con = useContext(Con); // 获取到Provider的值
return <div>子组件接受context {con.name}</div>;
}
useRef
在day02文章中,我们有接触到过useRef。此处不再讨论。
useMemo
useMemo是将某个函数返回值“放入到react底层原型链上,并返回该返回值的索引。该Hook主要是减少组件重新渲染时不必要的更新操作。useMemo的第二个参数是一个依赖项的数组,如果依赖项未发生改变,则直接使用保存在原型链上面的值,以此提高组件的性能。
import React from "react";
import { useMemo, useState } from "react";
export default function App() {
const [num, setNum] = useState(0);
const [age, setAge] = useState(100);
const total_sum = useMemo(() => {
console.log("memo");
let total = 0;
for (let i = 0; i < 1000; i++) {
total += i;
}
return total;
}, [num]);
return (
<div>
total_sum: {total_sum}
{/* 点击按钮组件会更新 useMemo会执行,因为依赖项num被修改了 */}
<button onClick={() => setNum(num + 1)}>num+1</button>
{/* 点击按钮组件会更新 但useMemo不会执行,因为依赖项num没被修改 */}
<button onClick={() => setAge(age + 1)}>age+1</button>
</div>
);
}
和React-redux相关的常用Hooks
useSelector
从redux的store当中获取到state
import React from 'react'
import { useSelector } from 'react-redux'
export const App = () => {
// 获取到state里面的theme属性
const theme = useSelector(state => state.theme)
return <div>{theme}</div>
}
useDispatch
执行store里面的action方法
import React from 'react'
import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
export const App = () => {
// 获取到state里面的theme属性
function changeTheme(color) {
// 执行store里面名为changeTheme的action
dispatch({ type:'changeTheme',payload:{ color } })
}
return <div>
<button onClick={changeTheme('red')}>修改theme为red</button>
</div>
}
和React-router(v6)相关的常用Hooks
useNavigate
实现路由跳转
import {useNavigate} from 'react-router-dom'
const navigate = useNavigate();
navigate('/login') // 跳转到login页面
navigate(1) // 前进一页
navigate(-1) // 后退一页
useParams
useParams获取路由的param参数
import { useParams } from "react-router-dom";
export default function ArticleDetail() {
let params = useParams();
// 获取文章id
return <h2>User: {params.id}</h2>;
}
useSearchParams
获取URL?后面的搜索(也就是query)参数
import { useSearchParams } from "react-router-dom";
export default function UserDetail() {
let [searchParams, setSearchParams] = useSearchParams();
// setSearchParams({"id":2}) 也可以这样设置url搜索参数-> 得到:http://URL/article?id=2
return <h2>User: {searchParams.id}</h2>; // 获取到搜索参数里面id的值
}
使用Hook的注意事项
- 只在React函数中调用Hook,比如React的函数组件中,自定义Hook中调用其他的Hook
- Hook需要在组件的最顶层调用