React Hooks 初学者指南
什么是 Hooks
React hooks 就像是 React 状态(海底)和功能组件的生命周期特性(船)之间的锚(就像船在海里扔下的东西,用来连接船和海底)。
- 仅适用于基于功能的组件,不适用于基于类的组件。
- 箭头和常规函数组件均可用
- 不能在循环、条件或嵌套函数内嵌套钩子
useState()
useState
hook 为你提供了设置变量状态的功能,并使用新状态自动更新 DOM
如何导入:
import React, {useState} from "react";
// or
React.useState;
例子useState
import React, { useState } from 'react'
let count1 = 0;
function App() {
const [count, setCount] = useState(0);
let count2 = 0;
return (
<div className='container mt-3'>
<h3>Hello World to React Hooks</h3>
<h4>Count : {count}</h4>
<h4>Count1 : {count1}</h4>
<h4>Count2 : {count2}</h4>
<button className='btn btn-info' onClick={() => {
setCount(count + 1);
console.log(`count : ${count} | count1 : ${count1} count2 :${count2}`);
count1 = count1 + 1;
count2 = count2 + 1;
}} >Add here</button>
</div>
)
}
export default App
在此代码片段中,count
和count1
都将作为变量更新,并且 中的 也会更新DOM
。但count2
始终为 1(因为 中的 +1 操作button.onClick
),因为每当 React 组件中的任何数据发生变化时,整个组件都会重新渲染。这就是组件存在的原因。
现在你可能会问,我们可以在全局状态中声明变量而不使用useState
。除了某些情况外,在所有编程语言中声明全局变量都被认为是不好的做法。参考:
- https://www.tutorialspoint.com/why-are-global-variables-bad-in-c-cplusplus
- https://dev.to/mervinsv/why-global-variables-are-bad-4pj
useState
即使组件重新渲染,也能提供一致的状态。
useState
对于对象
import React, { useState } from 'react'
function App() {
const [{ counter1, counter2 }, setCounter] = useState({ counter1: 0, counter2: 20 })
return (
<div className='container mt-3'>
<div className='container'>
<h3>Counter1 : {counter1}</h3>
<h3>Counter2 : {counter2}</h3>
{/* this doesnt not work becuz whenever you update state, you need to update the whole object */}
{/* Over here, we havent included the counter2 in the setCounter function. */}
<button className="btn btn-primary" onClick={() =>
setCounter(currentState => ({ counter1: currentState.counter1 + 1 }))}>Add</button>
{/* this will also not work because spread operator in objects comes first
unlike in functions, where spread operator comes last. */}
{/* Correct Code */}
<button className="btn btn-danger" onClick={() => setCounter(currentState => ({
...currentState,
counter1: currentState.counter1 - 1,
}))}>Subtract</button
</div>
</div>
)
}
export default App;
另一个例子useState()
import React, { useState } from "react";
function App() {
const [name, setName] = useState(localStorage.getItem("name") || "");
return (
<div className="App">
<div className="container mt-3">
<input name="name" value={name} onChange={e => {
setName(e.target.value)
localStorage.setItem("name", e.target.value)
}} className='form-control' />
<h3>Name : {name}</h3>
</div>
</div >
);
}
export default App;
useEffect()
- 每次渲染组件时执行
useEffect
当传递时,没有依赖项起作用componentDidMount
- 返回箭头函数
useEffect
是一个清理函数 - 多个
useEffect
hook 可以共存于一个组件中
import React, { useState, useEffect } from "react";
import HelloWorld from "./component/HelloWorld";
function App() {
const [count, setCount] = useState(0);
const [showComp, setComp] = useState(false);
useEffect(() => {
console.log("Rendering ...")
})
return (
<div className="App">
<br />
<div className='container mt-3'>
<h3>Count : {count}</h3>
<button className="btn btn-primary" onClick={() => setCount(count + 1)}>Add</button>
</div>
<br />
<div className='container'>
<button onClick={() => setComp(!showComp)} className="btn btn-info"> toggle </button>
{showComp && <HelloWorld />}
</div>
</div >
);
}
export default App;
// in src/component/HelloWorld.jsx
import React from 'react'
function HelloWorld() {
return (
<div className='container mt-3'>
<h2>HelloWorld component</h2>
</div>
)
}
export default HelloWorld
运行代码并查看控制台...无论您增加计数器还是切换组件,整个组件都会重新渲染。
要停止此操作,useEffect
请按如下方式修改
useEffect(() => {
console.log("Rendering ...")
}, [])
现在,只有刷新页面时,渲染才会打印到控制台上。请尝试修改代码,如下所示
useEffect(() => {
console.log("Rendering ...")
}, [count])
现在仅当计数更新时组件才会重新渲染
这就是我们的useEffect
做法,只在需要时更新/渲染组件。还有一种方法可以清理组件。尝试修改HelloWorld.jsx
import React from 'react'
function HelloWorld() {
React.useEffect(() => {
console.log('HelloWorld')
return () => {
console.log('GoodByeWorld')
}
}, [])
return (
<div className='container mt-3'>
<h2>HelloWorld component</h2>
</div>
)
}
export default HelloWorld
// and App.jsx
useEffect(() => {
console.log("Rendering ...")
},[])
现在尝试切换开关,您将看到组件已加载的消息DOM
以及其加载时间。其工作原理unmounting
类似于componentWillMount
componentWillUnmount
useRef
当你只是想把一些 html 元素或 React 组件放在焦点时
最好尝试运行此代码
import React, { useRef } from "react";
function App() {
const inputRef = useRef();
return (
<div className="App">
<div className="container mt-3">
<input ref={inputRef} name="name" value={name} onChange={e => {
setName(e.target.value)
localStorage.setItem("name", e.target.value)
}}
className='form-control'
/>
<br />
<button onClick={() => {
inputRef.current.focus();
}} className="btn btn-success" >Get focus</button>
</div>
</div>
);
}
export default App;
useReducer
图表最好地解释了这个钩子
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
default: return state;
}
}
function App() {
const [count, dispatch] = useReducer(reducer, 0);
return (
<div className="App">
<div className='container' >
<h2> count : {count} </h2>
<button onClick={() => dispatch({ type: 'increment' })} className='btn btn-primary' > increment </button>
<button onClick={() => dispatch({ type: 'decrement' })} className='btn btn-danger' > increment </button>
</div>
</div >
);
}
export default App;
useContext
图表最好地解释了这个钩子
// App.js
import React from 'react'
import HelloWorld from "./components/HelloWorld"
import About from './component/About'
import { UserContext } from './UserContext'
function App() {
return (
<div>
<UserContext.Provider value='super man'>
<HelloWorld />
</UserContext.Provider>
</div>
)
}
export default App
// Hello World component
import React, { useContext } from 'react'
import { UserContext } from '../UserContext'
function HelloWorld() {
const msg = useContext(UserContext);
return (
<div className='container mt-3'>
<h3>HelloWorld component : {msg}</h3>
</div>
)
}
export default HelloWorld
// About component
import React, { useContext } from 'react'
import { UserContext } from '../UserContext'
function About() {
const msg = useContext(UserContext);
return (
<div className='container mt-3'>
<h3>About component : {msg}</h3>
</div>
)
}
export default About
// Usercontext.js
import { createContext } from "react";
export const UserContext = createContext(null);
useMemo
备忘录或记忆化是指记住某件事的结果,而不是在需要时一遍又一遍地计算它(直到没有改变)
useMemo
在 React 中,useEffect 用于那些开销较大且我们不希望它们反复运行的函数。它与useEffect
Hook 类似,但更多地用于函数。而 useEffect 则用于管理组件生命周期中的状态,尽管它们非常相似。
import React from 'react'
function expensivePhoneFunc (product) {
console.log("expensivePhoneFunc")
return product[0];
}
function App() {
let product = [{
name: 'Phone XL',
price: 100
},
{
name: 'Phone Mini',
price: 80
},
{
name: 'Phone Standard',
price: 60
}]
const [count , setCount] = React.useState(0);
const expensivePhone = React.useMemo( () => {
return expensivePhoneFunc(product);
},[])
return (
<div className='container mt-3'>
<h3>Product : {expensivePhone.name}</h3>
<h4>Price : {expensivePhone.price}</h4>
<br />
<h3>Count : {count}</h3>
<button className='btn btn-primary' onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
export default App
useCallback
它是一种useMemo
替代方法,但适用于函数,而不是函数返回的结果。它不需要反复运行函数。它通常与 useMemo 一起使用。
import React, {useCallback} from 'react'
import HelloWorld from './component/HelloWorld'
function App() {
const [count, setCount] = React.useState(0);
const increment = useCallback(() => {
setCount(c => c + 1);
}, [setCount]);
return (
<div>
<HelloWorld increment={increment} />
<h3>Count : {count}</h3>
</div>
)
}
export default App
// HelloWorld.jsx
import React from 'react'
const HelloWorld = React.memo(({ increment }) => {
console.log("hello")
return (
<div className='container mt-3'>
<h3>HelloWorld component</h3>
<button onClick={increment}>Hello World</button>
</div>
)
})
export default HelloWorld
文章来源:https://dev.to/dev117uday/react-hooks-0-to-hero-4b7o