沉迷于 React Hooks
每当我读到关于 React 的新功能时,我都会全力以赴。我会努力理解它的含义,以及如何将它应用到我的项目中。
Hooks
是 React 的最新功能之一。它是在 React 16.8 中引入的,它允许你在非类组件中使用状态和生命周期功能。
要使用 React Hooks,您需要将 React 更新到 16.8 或更高版本。如果您使用了 React Hooks,请不要忘记同时更新react-dom
和react-hot-loader
。如果不更新热加载器,您可能会遇到一些奇怪的错误。
国家钩子
您可能知道,在 16.8 之前,如果您想使用状态,则需要执行如下操作:
import React, {Component} from 'react';
class Something extends Component {
constructor(props){
super(props);
this.state = {
foo: 'bar'
}
this.setFoo = this.setFoo.bind(this);
}
setFoo(newFoo) {
this.setState({...this.state, foo: newFoo});
}
render() {
const {foo} = this.props;
return (
<div>
<span>{foo}</span>
<button onClick={this.setFoo('theRealFoo')}>Set Foo</button>
</div>
);
}
}
export default Something;
此示例包含大量使 JavaScript 类正常工作所需的代码。为了this
在正确的上下文中使用,您需要特别注意该函数。相同示例使用钩子后如下所示:
import React, {useState} from 'react';
const Something = () => {
const [foo, setFoo] = useState("bar");
return (
<div>
<span>{foo}</span>
<button onClick={setFoo('theRealFoo')}>Set Foo</button>
</div>
);
}
export default Something;
如你所见,代码量明显减少。本例中useState
用到了 React 提供的钩子函数。它是一个返回状态值的函数,以及一个用于更新状态值的函数。
所以不再需要了setState
。React 会处理好一切。
useState
采用一个参数,该参数是状态字段的初始值。
现在,状态有时会变得复杂,你可能需要多个变量。为了管理这种情况,你可以使用多个useState
调用来创建多个状态变量,或者像以前使用类一样使用对象。
我尝试了这两种方法,我认为单个变量更容易处理,因为您不需要像以前那样进行所有嵌套对象的合并。
React 还有另一个名为 的钩子userReducer
,当你需要处理复杂的状态时,它会非常方便。与 Redux 类似,你可以使用一个函数,该函数接受状态参数和一个 action 来更新状态。
import React, {useReducer} from 'react';
const csReducer = (state, action) => {
switch(action.type) {
case 'foo': {
return 'foo';
}
case 'bar': {
return 'bar';
}
default: {
return state;
}
}
}
const ComplexSomething = (props) => {
const [someState, dispatch] = useReducer(csReducer, props.someState);
return (
<div>
<span>{someState}</span>
<button onClick={dispatch({type: 'foo'})}>Say Foo!</button>
<button onClick={dispatch({type: 'bar'})}>Say Bar!</button>
</div>
)
}
export default ComplexSomething;
您可以看到整个状态处理都在 中csReducer
。它执行操作,并根据类型返回状态的另一个值。您还可以发送有效负载{type: 'someAction', payload: 'foobar!'}
,将获取的信息放入状态中。如果您按给定的方式返回状态,则 React 不会更改状态,也不会触发重新渲染。
如你所见,状态处理相当出色。你可以用 做一些简单的操作useState
,也可以用 来提升难度useReducer
。
带钩子的生命周期
现在我们可以处理状态了,你可能还记得我之前提到过可以用钩子来处理生命周期操作。我们来聊聊这个。
有一个名为 的钩子useEffect
。之所以这样称呼它,是因为你在生命周期方法中所做的大多数事情,例如获取数据、向 DOM 添加事件或诸如此类的事情,都被称为“副作用”,因此useEffect
。
让我们举一个例子:
import React, {useState, useEffect} from 'react';
import Spinner from './Spinner';
const EffectComponent = (props) => {
const [pending, setPending] = useState(true);
const [product, setProduct] = useState({});
useEffect(() => {
setPending(true);
fetch(`https://myapi.api/product/${props.id}`).then((productData) => {
setProduct(productData);
setPending(false);
})
}, [props.id]);
if(pending === true) return <Spinner />
return (
<div>{product.name}</div>
)
}
export default EffectComponent;
首先,我们定义两个状态变量pending
和product
。然后我们使用useEffect
来获取数据。该函数设置pending
为 true,然后从 API 加载数据。数据到达后,它将product
状态 和 设置pending
为 false,这样我们的组件就会渲染。
effect
每次组件渲染时都会调用 。如果您之前使用过,componentDidMount
那么componentWillUpdate
您就会知道,管理何时应该加载数据以及何时不应该加载数据是一件非常麻烦的事情。有useEffect
一个简单的解决方案。第二个参数是一个包含变量的数组。effect
只有当数组中的变量发生变化时, 才会触发 。在上面的例子中,我使用了[props.id]
,所以effect
只会在每次props.id
变量发生变化时触发。
您还可以返回一个函数,useEffect
该函数将在组件卸载时调用。您可以在那里进行一些清理工作。
共享状态逻辑的新方法
在 React 的最新版本中,有两种方法可以在组件之间共享状态逻辑:Render Props 和高阶组件。这两种方法都很好用,但要理解它们的概念……我试着向不同的人解释了上百遍,大概只有 50% 的人理解了。
使用 React 16.8 时,你可以使用 hooks 在组件之间共享状态逻辑。这种方式更简单,因为 hooks 本身就是一个函数……而且大家都理解函数,对吧?
为了共享逻辑,我们可以构建自己的自定义钩子并调用它们,就像调用预建的钩子一样。
import React, {useState} from 'react';
// This is the custom hook
function useFetchData(url, setData) {
const [pending, setPending] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setPending(true);
fetch(url).then((productData) => {
setData(productData);
setPending(false);
}).catch(error =>{
setError(error);
})
}, [url]);
return {pending, error};
}
const EffectComponent = (props) => {
const [product, setProduct] = useState({});
// and here we call it.
const {pending, error} = useFetchData(`https://myapi.api/product/${props.id}`, setProduct);
if(pending === true) return <Spinner />
return (
<div>
<span>{product.name}</span>
{error && <span class="error">{error}</span>}
</div>
)
}
export default EffectComponent;
看看上面的例子。我的做法是把获取数据的逻辑去掉,放到一个函数里,这个函数就是自定义钩子。惯例是每个钩子都以 开头use
,然后是你的函数名。现在我可以直接使用自定义钩子,而不用重写获取数据的逻辑useFetchData
。
我觉得这个概念更容易理解。你只需要把所有东西都放进一个函数里,调用hook
它就行了。
钩子的规则
现在,在使用 Hooks 之前,你需要了解一些具体的事情。React 团队称之为“Hooks 规则”。
事实上只有两种:
1.) 只能在函数组件中使用 hooks
此规则有一个例外。你可以在自定义钩子中使用钩子。
2.) 您不能在任何类型的循环、嵌套函数或条件中使用钩子。
最后一个是因为 React 会记住您使用钩子的顺序,并使用此顺序为您提供正确的数据或执行正确的操作。
例如:
const [varA, setVarA] = useState(1);
const [varB, setVarB] = useState(2);
const [varC, setVarC] = useState(3);
这没问题。每当调用该组件时,钩子的顺序都是一样的。每当使用 时varC
,它的值都是 3。
const [varA, setVarA] = useState(1);
if(varA === 2) {
const [varB, setVarB] = useState(2);
}
const [varC, setVarC] = useState(3);
这有问题。如果 momentvarA
为 2,钩子的顺序就会改变,所以会出错。
还有其他
是的,还有其他的……事实上,还有很多其他的 React Hooks。但我认为它们更适合用于边缘情况,你可能不经常用到,甚至根本不需要。useMemo
如果你想在组件渲染过程中的函数中执行一些繁重的操作,它可能会派上用场。它接受一个函数和一个值数组作为参数。只有当数组中的值发生变化时,该函数才会运行。否则,它将返回已记忆的值。你可以在这里阅读关于记忆化的内容。
不过,有一个 hook 看起来确实很有意思。useContext
我还需要对它进行更多测试,但我认为它会比现在更容易地使用 React 的 Context API。
好了,这篇文章就到这里。希望你学到了一些东西,也希望你至少享受了一点。如果你有什么建议或意见,欢迎随时留言。
感谢您的阅读!
文章来源:https://dev.to/markusclaus/hooked-on-react-hooks-36kh