useState + useEffect + 自定义 Hook

一句话:useState用于管理组件状态,useEffect用于处理副作用,自定义Hook是复用状态逻辑的机制。

1. useState

1.1 什么是useState?


graph LR

    A[useState] --> B[状态管理]

    A --> C[返回值]

    A --> D[更新函数]

    style A fill:#e1f5fe

useState:React Hook,用于在函数组件中添加状态。

1.2 基础用法

 
import { useState } from 'react';
 
function Counter() {
 
    const [count, setCount] = useState(0);
 
    return (
 
        <div>
 
            <p>Count: {count}</p>
 
            <button onClick={() => setCount(count + 1)}>
 
                Increment
 
            </button>
 
        </div>
 
    );
 
}
 

1.3 函数式更新

 
// 函数式更新
 
function Counter() {
 
    const [count, setCount] = useState(0);
 
    // 好:使用函数式更新
 
    const increment = () => {
 
        setCount(prev => prev + 1);
 
    };
 
    // 不好:直接使用当前值
 
    const incrementBad = () => {
 
        setCount(count + 1); // 可能有问题
 
    };
 
    return (
 
        <div>
 
            <p>Count: {count}</p>
 
            <button onClick={increment}>Increment</button>
 
        </div>
 
    );
 
}
 

2. useEffect

2.1 什么是useEffect?


graph LR

    A[useEffect] --> B[副作用处理]

    A --> C[依赖数组]

    A --> D[清理函数]

    style A fill:#e8f5e8

useEffect:React Hook,用于处理组件的副作用(如数据获取、订阅、手动修改DOM等)。

2.2 基础用法

 
import { useState, useEffect } from 'react';
 
function UserProfile({ userId }) {
 
    const [user, setUser] = useState(null);
 
    useEffect(() => {
 
        // 数据获取
 
        fetch(`/api/users/${userId}`)
 
            .then(response => response.json())
 
            .then(data => setUser(data));
 
    }, [userId]); // 依赖数组
 
    if (!user) return <div>Loading...</div>;
 
    return (
 
        <div>
 
            <h1>{user.name}</h1>
 
            <p>{user.email}</p>
 
        </div>
 
    );
 
}
 

2.3 清理函数

 
import { useEffect } from 'react';
 
function Timer() {
 
    useEffect(() => {
 
        const interval = setInterval(() => {
 
            console.log('Timer tick');
 
        }, 1000);
 
        // 清理函数
 
        return () => {
 
            clearInterval(interval);
 
        };
 
    }, []); // 空依赖数组
 
    return <div>Timer running...</div>;
 
}
 

3. 自定义Hook

3.1 什么是自定义Hook?


graph LR

    A[自定义Hook] --> B[复用状态逻辑]

    A --> C[以use开头]

    A --> D[可组合]

    style A fill:#e1f5fe

自定义Hook:以”use”开头的函数,用于复用状态逻辑。

3.2 基础示例

 
import { useState, useEffect } from 'react';
 
// 自定义Hook:获取用户信息
 
function useUser(userId) {
 
    const [user, setUser] = useState(null);
 
    const [loading, setLoading] = useState(true);
 
    const [error, setError] = useState(null);
 
    useEffect(() => {
 
        setLoading(true);
 
        setError(null);
 
        fetch(`/api/users/${userId}`)
 
            .then(response => {
 
                if (!response.ok) {
 
                    throw new Error('Failed to fetch user');
 
                }
 
                return response.json();
 
            })
 
            .then(data => {
 
                setUser(data);
 
                setLoading(false);
 
            })
 
            .catch(err => {
 
                setError(err.message);
 
                setLoading(false);
 
            });
 
    }, [userId]);
 
    return { user, loading, error };
 
}
 
// 使用自定义Hook
 
function UserProfile({ userId }) {
 
    const { user, loading, error } = useUser(userId);
 
    if (loading) return <div>Loading...</div>;
 
    if (error) return <div>Error: {error}</div>;
 
    return (
 
        <div>
 
            <h1>{user.name}</h1>
 
            <p>{user.email}</p>
 
        </div>
 
    );
 
}
 

3.3 多个自定义Hook组合

 
// 自定义Hook:表单处理
 
function useForm(initialValues) {
 
    const [values, setValues] = useState(initialValues);
 
    const handleChange = (e) => {
 
        const { name, value } = e.target;
 
        setValues(prev => ({
 
            ...prev,
 
            [name]: value
 
        }));
 
    };
 
    const reset = () => {
 
        setValues(initialValues);
 
    };
 
    return { values, handleChange, reset };
 
}
 
// 自定义Hook:提交处理
 
function useSubmit(onSubmit) {
 
    const [isSubmitting, setIsSubmitting] = useState(false);
 
    const handleSubmit = async (values) => {
 
        setIsSubmitting(true);
 
        try {
 
            await onSubmit(values);
 
        } finally {
 
            setIsSubmitting(false);
 
        }
 
    };
 
    return { handleSubmit, isSubmitting };
 
}
 
// 组合使用
 
function LoginForm() {
 
    const { values, handleChange } = useForm({
 
        email: '',
 
        password: ''
 
    });
 
    const { handleSubmit, isSubmitting } = useSubmit(async (values) => {
 
        await login(values);
 
    });
 
    return (
 
        <form onSubmit={(e) => {
 
            e.preventDefault();
 
            handleSubmit(values);
 
        }}>
 
            <input
 
                name="email"
 
                value={values.email}
 
                onChange={handleChange}
 
            />
 
            <input
 
                name="password"
 
                type="password"
 
                value={values.password}
 
                onChange={handleChange}
 
            />
 
            <button type="submit" disabled={isSubmitting}>
 
                {isSubmitting ? 'Logging in...' : 'Login'}
 
            </button>
 
        </form>
 
    );
 
}
 

4. 实际案例

4.1 数据获取Hook

 
function useFetch(url) {
 
    const [data, setData] = useState(null);
 
    const [loading, setLoading] = useState(true);
 
    const [error, setError] = useState(null);
 
    useEffect(() => {
 
        const abortController = new AbortController();
 
        setLoading(true);
 
        setError(null);
 
        fetch(url, { signal: abortController.signal })
 
            .then(response => {
 
                if (!response.ok) {
 
                    throw new Error('Network response was not ok');
 
                }
 
                return response.json();
 
            })
 
            .then(data => {
 
                setData(data);
 
                setLoading(false);
 
            })
 
            .catch(err => {
 
                if (err.name !== 'AbortError') {
 
                    setError(err.message);
 
                    setLoading(false);
 
                }
 
            });
 
        return () => {
 
            abortController.abort();
 
        };
 
    }, [url]);
 
    return { data, loading, error };
 
}
 
// 使用
 
function UserList() {
 
    const { data: users, loading, error } = useFetch('/api/users');
 
    if (loading) return <div>Loading...</div>;
 
    if (error) return <div>Error: {error}</div>;
 
    return (
 
        <ul>
 
            {users.map(user => (
 
                <li key={user.id}>{user.name}</li>
 
            ))}
 
        </ul>
 
    );
 
}
 

5. 常见坑点

1. 依赖数组问题

 
// 问题:缺少依赖
 
useEffect(() => {
 
    fetchData(userId);
 
}, []); // 缺少userId
 
// 解决:添加所有依赖
 
useEffect(() => {
 
    fetchData(userId);
 
}, [userId]);
 

2. 无限循环

 
// 问题:useEffect导致无限循环
 
useEffect(() => {
 
    setCount(count + 1); // 每次渲染都更新状态
 
});
 
// 解决:使用依赖数组
 
useEffect(() => {
 
    setCount(prev => prev + 1);
 
}, [dependency]);
 

核心要点

 
// useState
 
const [count, setCount] = useState(0);
 
// useEffect
 
useEffect(() => {
 
    // 副作用
 
    return () => {
 
        // 清理
 
    };
 
}, [dependencies]);
 
// 自定义Hook
 
function useCustomHook() {
 
    const [state, setState] = useState(initial);
 
    // ...
 
    return { state, setState };
 
}
 

速记卡(面试闪卡)

Q1:一句话讲清「useState + useEffect + 自定义 Hook」到底是什么?

A:useState 管组件状态、useEffect 管副作用、自定义 Hook 是以 use 开头复用状态逻辑的函数。

Q2:1. useState —— 怎么理解?

A:useState 像”组件的小账本”:const [count,setCount]=useState(0) 拿到状态值和改状态的笔;函数式更新 setCount(prev=>prev+1) 才稳妥(state hook 状态钩子)。

Q3:2. useEffect —— 怎么理解?

A:useEffect 像”副作用管家”:数据获取、订阅、改 DOM 都放这;依赖数组控制何时跑,return 清理函数负责收尾(side effect 副作用)。

Q4:3. 自定义Hook —— 怎么理解?

A:自定义 Hook 像”可复用的逻辑积木”:以 use 开头,内部用 useState/useEffect,返回 {data,loading},多个组合搭出复杂功能(custom hook 自定义钩子)。

Q5:5. 常见坑点 —— 怎么理解?

A:两坑要记:依赖数组漏写 userId 导致用旧值;useEffect 里直接 setCount(count+1) 会无限循环,要用依赖或函数式更新(infinite loop 无限循环)。

Q6:核心速记主线有哪些?

  • useState:返回 [值, 改值的函数],函数式更新避免闭包旧值

  • useEffect:管副作用,依赖数组控时机,return 做清理

  • 自定义 Hook:use 开头复用状态逻辑,可组合

  • 坑点:依赖别漏写;effect 里 setState 要带依赖否则死循环

口诀

A:useState 小账本,值配改笔 useState;

useEffect 管家,副作用里收尾清;

自定义 Hook 积木,use 开头逻辑复;

依赖别漏写,effect 防死循环。

相关链接