React组件复用:自定义Hook减少80%重复代码(react组件constructor) 99xcs.com

一、3个高频场景实战:从重复代码到Hook封装

场景1:数据请求逻辑复用(useFetch)

问题:多个组件重复编写“加载中/错误处理/数据缓存”的API请求逻辑。

重复代码示例

javascript

// 组件A:获取用户数据

function UserProfile() {

const [user, setUser] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

fetch('/api/user')

.then(res => res.json())

.then(data => { setUser(data); setLoading(false); })

.catch(err => { setError(err); setLoading(false); });

}, []);

// ...渲染UI

}

// 组件B:获取商品列表(重复相同逻辑)

function ProductList() {

const [products, setProducts] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

fetch('/api/products')

.then(res => res.json())

.then(data => { setProducts(data); setLoading(false); })

.catch(err => { setError(err); setLoading(false); });

}, []);

// ...渲染UI

}

自定义Hook封装(useFetch)

javascript

// hooks/useFetch.js

function useFetch(url) {

const [data, setData] = useState(null);

const [loading, setLoading] = useState(true);

const [error, setError] = useState(null);

useEffect(() => {

const fetchData = async () => {

try {

const res = await fetch(url);

const result = await res.json();

setData(result);

} catch (err) {

setError(err);

} finally {

setLoading(false);

}

};

fetchData();

}, [url]);

return { data, loading, error }; // 返回状态供组件使用

}

组件使用Hook后

javascript

// 组件A:获取用户数据(一行调用)

function UserProfile() {

const { data: user, loading, error } = useFetch('/api/user');

// ...渲染UI

}

// 组件B:获取商品列表(一行调用)

function ProductList() {

const { data: products, loading, error } = useFetch('/api/products');

// ...渲染UI

}

---

场景2:表单处理逻辑复用(useForm)

问题:多个表单组件重复处理“输入值绑定、表单提交、重置”逻辑。

自定义Hook封装(useForm)

javascript

// hooks/useForm.js

function useForm(initialValues) {

const [values, setValues] = useState(initialValues);

// 处理输入变化

const handleChange = (e) => {

const { name, value } = e.target;

setValues({ ...values, [name]: value });

};

// 重置表单

const resetForm = () => setValues(initialValues);

return { values, handleChange, resetForm };

}

组件使用

javascript

function LoginForm() {

const { values, handleChange, resetForm } = useForm({

username: '',

password: ''

});

const handleSubmit = (e) => {

e.preventDefault();

console.log('提交数据:', values);

};

return (

<form onSubmit={handleSubmit}>

<input

name="username"

value={values.username}

onChange={handleChange}

placeholder="用户名"

/>

<input

name="password"

type="password"

value={values.password}

onChange={handleChange}

placeholder="密码"

/>

<button type="submit">登录</button>

<button type="button" onClick={resetForm}>重置</button>

</form>

);

}

---

场景3:窗口大小监听复用(useWindowSize)

问题:多个组件需要监听窗口大小变化(如响应式布局),重复编写useEffect+事件监听。

自定义Hook封装(useWindowSize)

javascript

// hooks/useWindowSize.js

function useWindowSize() {

const [size, setSize] = useState({

width: window.innerWidth,

height: window.innerHeight

});

useEffect(() => {

const handleResize = () => {

setSize({

width: window.innerWidth,

height: window.innerHeight

});

};

window.addEventListener('resize', handleResize);

return () => window.removeEventListener('resize', handleResize); // 清理

}, []);

return size;

}

组件使用

javascript

function ResponsiveComponent() {

const { width, height } = useWindowSize();

return (

<div>

窗口大小:{width}x{height}

{width < 768 ? <MobileUI /> : <DesktopUI />}

</div>

);

}

---

二、自定义Hook核心原则

  1. 命名规范:必须以use开头(如useFetch),确保React能识别Hook规则。
  2. 单一职责:一个Hook只封装一个逻辑(如useFetch只处理数据请求,不掺杂表单逻辑)。
  3. 依赖清晰:Hook内部使用useEffect等时,需正确声明依赖数组(如[url]),避免无限循环。

---

三、对比传统复用方案(HOC/Render Props)