写react也有一段时间了,封装过一些共有组件,react的核心理念之一就是追求组件的复用性,这样一来,一旦做了大量组件的复用,就会面临着state的杂乱无章,时间久了,维护成本很高,这不是所希望看到的,react hooks就是为了解决这一问题,可以复用组件的逻辑而剥离组件的状态。
- State Hook
import React, {useState} from 'react';
import './App.css';
export default function App(props) {
const [count, setCount] = useState(0);
console.log(useState);
console.log(useState(0));
console.log(count)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};
在以往写react时,编写组件使用较多的是classComponent (类组件),这种组件有state,有生命周期,而 React Hooks 是可以在函数组件中实现 state 和生命周期,并且使得一切更加简洁,复用性更高。
useState(0)传入count的初始值。
setCount只会更改count,即传入值为下一次的count值。
在使用react hook时要注意:
- 只能在顶层调用Hooks 。不要在循环,条件或嵌套函数中调用Hook
- 只能在functional component(函数组件)中使用。
- Effect Hook
看了react的官方文档,文中提到了类组件中的 componentDidUpdate(object prevProps, object prevState) 这个生命周期函数,该函数会在除了初始化render以外的任何一次render调用,其实就已经有一点钩子的味道在里面了。 使用该方法可以在组件更新之后操作 DOM 元素。
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
// 通过浏览器自带的 API 更新页面标题
document.title = `You clicked ${count} times`;
return function cleanup() {
// 清理时执行
};
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
这里的useEffect的触发时机是每一次render(包括第一次初始化render)。
可以看到调用浏览器的api,更新了title。
值得注意的是,如果在 useEffect 中返回一个函数,那么react将会在清理时执行它,相当于
在componentWillUnmount 这个生命周期函数执行该函数。
- Hooks 规范
可以安装 ESLint 插件来规范hooks的使用
cnpm install eslint-plugin-react-hooks@next
现在来解释为什么只能在顶层调用Hooks ,我们改造一下之前的代码
import React, {useState, useEffect} from 'react';
import './App.css';
export default function App(props) {
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
useEffect(() => {
// 通过浏览器自带的 API 更新页面标题
document.title = `You clicked ${count} times`;
// return function cleanup() {
// ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
// };
});
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
if (count < 2) {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
console.log(name, surname);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => {
setCount(count + 1);
setName(name);
}}>
Click me
</button>
</div>
);
};
当count<2时执行第二个hook,当我们点击两次button之后,浏览器报错了
是因为前后两次hook的顺序不一致(至于为什么报错,原理还在研究中)
官方给出的解释是
并且给出正确写法,将条件语句放进hook内部
useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (count < 2) {
localStorage.setItem('formData', name);
}
});