写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 [email protected]

现在来解释为什么只能在顶层调用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的顺序不一致(至于为什么报错,原理还在研究中)

官方给出的解释是


React wouldn’t know what to return for the second useState Hook call. React expected that the second Hook call in this component corresponds to the persistForm effect, just like during the previous render, but it doesn’t anymore. From that point, every next Hook call after the one we skipped would also shift by one, leading to bugs.
This is why Hooks must be called on the top level of our components. If we want to run an effect conditionally, we can put that condition inside our Hook:

并且给出正确写法,将条件语句放进hook内部

useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (count < 2) {
localStorage.setItem('formData', name);
}
});

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注