react hook 详解及使用
1、hooks 是什么Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。2、为什么要使用 hooks难以理解的 class
1、hooks 是什么
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
2、为什么要使用 hooks
-
难以理解的 class 、组件中必须去理解 javascript 与 this 的工作方式、需要绑定事件处理器、纯函数组件与 class 组件的差异存在分歧、甚至需要区分两种组件的使用场景;Hook 可以使你在非 class 的情况下可以使用更多的 React 特性。
-
在组件之间复用状态逻辑很难、大型组件很难拆分和重构,也很难测试。Hook 使你在无需修改组件结构的情况下复用状态逻辑。
-
复杂组件变得难以理解、业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
3、react 中我们常用的 hook
什么是 Hook?
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
下面我们挑几种介绍一下(useState、userContext、useEffect )
1) useState 状态钩子
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
等价的 class 示例
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
</div>
);
}
}
什么时候我会用 Hook? 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。
在 useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值。类似 this.state,第二项是一个函数,用来更新状态,类似 setState。
上述例子中没有 class 继承、没有 this、没有生命周期、代码更加简洁、这就是使用 hooks 的意义;
2)useContext() 共享状态钩子
如果需要在深层次组件之间共享状态,可以使用 useContext()。context 提供了一种在组件之间共享 props 的方式,而不必显示地通过组件树的逐层传递 props; useContext 钩子比原始 class 组件中使用 context 更为方便;
假设现在有俩个组件 Navbar 和 Messages,我们希望它们之间共享状态。
<div className="test">
<Navbar />
<Messages />
</div>
使用方法如下: 第一步在它们的父组件上使用 React 的 Context API,在组件外部建立一个 Context。
import React, { useContext } from 'react';
const TestContext = React.createContext({});
const Navbar = () => {
const { username } = useContext(TestContext);
return (
<div className="navbar">
<p>{username}</p>
</div>
);
};
const Messages = () => {
const { messageDetail } = useContext(TestContext);
return (
<div className="messages">
<p>1 message for {messageDetail}</p>
</div>
);
};
const App = () => {
return (
<TestContext.Provider
value={{
username: 'superawesome',
messageDetail: 'hello world',
}}
>
<div className="test">
<Navbar />
<Messages />
</div>
</TestContext.Provider>
);
};
export default App;
等价的 class 用例
import React from 'react';
const TestContext = React.createContext({});
const Navbar = () => (
<TestContext.Consumer>
{({ username }) => (
<div className="messages">
<p>1 message for {username}</p>
</div>
)}
</TestContext.Consumer>
);
const Messages = () => {
return (
<TestContext.Consumer>
{({ messageDetail }) => (
<div className="messages">
<p>1 message for {messageDetail}</p>
</div>
)}
</TestContext.Consumer>
);
};
class App extends React.Component {
render() {
return (
<TestContext.Provider
value={{
username: 'superawesome',
messageDetail: 'hello world',
}}
>
<div className="test">
<Navbar />
<Messages />
</div>
</TestContext.Provider>
);
}
}
export default App;
两种用法相比较 hooks 更加简介易懂、render 中也没有函数式写法!
3)useEffect 副作用钩子
它可以用来更好的处理副作用,如异步请求等;可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
useEffect(() => {}, [array]);
使用 useEffect 更新网页 title
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
},[count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
使用 class (等价)
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
</div>
);
}
}
useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出 Effect 的依赖项。只要这个数组发生变化,useEffect()就会执行。当第二项省略不填时,useEffect()会在每次组件渲染时执行。这一点类似于类组件的 componentDidMount。
我们实现一个 useEffect() 依赖项变化的例子
import React, { useState, useEffect } from 'react';
const AsyncCount = ({ countNum }) => {
const [loading, setLoading] = useState(true);
const [count, setCount] = useState(0);
useEffect(() => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setCount(countNum);
}, 2000);
}, [countNum]);
return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};
const TestCount = () => {
const [count, setCount] = useState(0);
const changeCount = (name) => {
setCount(name);
};
return (
<>
<AsyncCount countNum={count} />
<button
onClick={() => {
changeCount(count + 1);
}}
>
增加
</button>
<button
onClick={() => {
changeCount(count - 1);
}}
>
减少
</button>
</>
);
};
export default TestCount;
再上述例子中,我们把处理 count 异步的操作以及是否渲染 loading,都放在了 AsyncCount hook 中;把复杂操作,通过 hooks 提取出去;将组件中关联部分拆分;
那下面我们在做一个更加细化的拆分,拆出一个自己的 hook
import React, { useState, useEffect } from 'react';
const useCount = (countNum) => {
const [loading, setLoading] = useState(true);
const [count, setCount] = useState(0);
useEffect(() => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setCount(countNum);
}, 2000);
}, [countNum]);
return [loading, count];
};
const AsyncCount = ({ countNum }) => {
const [loading, count] = useCount(countNum);
return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};
const TestCount = () => {
const [count, setCount] = useState(0);
const changeCount = (count) => {
setCount(count);
};
return (
<>
<AsyncCount countNum={count} />
<button
onClick={() => {
changeCount(count + 1);
}}
>
增加
</button>
<button
onClick={() => {
changeCount(count - 1);
}}
>
减少
</button>
</>
);
};
export default TestCount;
我们在上述 AsyncCount 组件中再次将它的副作用操作拆分;在此组件中只关注渲染结果,useCount 接受一个数字,返回一个数组,数组中包括状态,与 count 两个结果;在我们使用 useCount 时,会根据我们传入参数的不同而返回不同的状态;
另外关于 hook 其他注意事项、在使用 useState 和一些其他钩子时、它们彼此的对应关系、是依靠 Hook 调用的顺序、所以我们不要在循环、条件、或嵌套函数中使用 hook、确保总是在你的 React 函数的最顶层调用他们,这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确;
用代码来解释一下顶层的意思
if (count !== 0) {
useEffect(function persistForm() {
...
});
}
这样会导致在 count 不等于 0 时,条件值为 true,会执行此次 hook,但如果在某次渲染时、如 count 值变为 0,那么此次 hook 将不在渲染,从而导致 hook 顺序发生了改变,产生不可预知的 bug;
其次在 hooks 中在 setState 值为 Object 或 Array 时,由于为引用类型,setState 通过回调函数的形式赋值,其参数值存的是 obj 的地址,由于 react 中 state 是只读的,所以在操作本身值改变是无法成功的,所以我们应当拷贝出去一份,在重新赋值;还有 hooks 异步获取当前 state 值问题等等,遇到问题多百度;
它为我们本来的开发带来了很大的变化、更加便于我们操作使用;come on!
更多推荐
所有评论(0)