Hook 的特性:在编写class 的情况下,使用state 以及其他React 特性(比如生命周期)
类组件相比于函数式组件的优势:
import { memo, useState } from "react"
function CounterHook(props) {
const [ counter, setCounter ] = useState(0)
function decrement() {
setCounter(counter - 1)
}
return (
<div>
<h2>当前计数: {counter}</h2>
<button onClick={e=>setCount(count+1)}>+1</button>
<button onClick={decrement}>-1</button>
</div>
)
}
export default memo(CounterHook)
// 在组件第一次渲染、组件更新时都会执行
useEffect(() => {
// 当前传入的回调函数会在组件被渲染完成后,会自动执行
// 网络请求/DOM操作/事件监听
})
useEffect(() => {
console.log("")
// 回调函数:回调函数 => 组件被重新渲染或者组件卸载的时候执行
return () => {
console.log("")
}
})
useEffect(() => {
console.log("修改state")
})
useEffect(() => {
console.log("监听redux 中的数据")
return () => {
// 取消redux 中数据的监听
}
})
useEffect(() => {
console.log("在组件第一次渲染时执行一次")
return () => {
console.log("在组件卸载时执行一次")
}
}, [])
useEffect(() => {
console.log("count 发生了变化")
}, [count])
组件中使用共享的Context 有两种方式:
注意事项:
// src/context/index.js
import { createContext } from "react"
const UserContext = createContext()
const ThemeContext = createContext()
export {
UserContext,
ThemeCoontext
}
// src/index.js
import { UserContext, ThemeContext } from "@/context/index.js"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<UserContext.Provider value={{ name: "why", age: 18 }}>
<ThemeContext.Provider value={{ color: "red", size: 30 }}>
<App/>
</ThemeContext.Provider>
</UserContext.Provider>
);
// src/App.jsx
import { memo, useContext } from 'react'
import { UserContext, ThemeContext } from "@/context/index.js"
const App = memo(() => {
// 使用context
const user = useContext(UserContext)
const theme = useContext(ThemeContext)
return (
<div>
<h2>{ user.name } - { user.age }</h2>
<div style={{ color: theme.color, fontSize: theme.size }}>主题</div>
</div>
)
})
export default App
import { useReducer } from 'react'
function reducer(state, action) {
switch(action.type) {
case "increment"
return { ...state, counter: state.counter + 1 }
case "decrement"
return { ...state, counter: state.counter - 1 }
case "add_number"
return { ...state, counter: state.counter + action.num }
case "sub_number"
return { ...state, counter: state.counter - action.num }
}
}
const App = memo(() => {
const [state,dispatch] = useReducer(reducer, {counter: 0})
return (
<div>
<h2>{state.counter}</h2>
<button onClick={e => dispatch({ type: "increment" })}>+1</button>
<button onClick={e => dispatch({ type: "decrement" })}>-1</button>
<button onClick={e => dispatch({ type: "add_number", num: 5 })}>+5</button>
<button onClick={e => dispatch({ type: "sub_number", num: 5 })}>-5</button>
</div>
)
})
当修改count 时,会影响子组件HYHome 重新渲染,当修改message 时,子组件不重新渲染
import { useCallback, useRef } from 'react'
const HYHome = memo(function(props) {
// props 中的属性发生改变时,组件本身就会被重新渲染
const { increment } = props
console.log("HYHome 被渲染")
return (
<div>
<button onClick={increment}>increment + 1</button>
</div>
)
})
const App = memo(function() {
const [count, setCount] = useState(0)
const [message, setMessage] = useState("hello")
// 1. 闭包陷阱
const increment = useCallback(function foo() {
console.log("increment")
setCount(count + 1)
}, [count])
// 2. 使用useRef 实现
const countRef = useRef()
countRef.current = count
const increment = useCallback(function foo() {
console.log("inncrement")
setCount(countRef.current + 1)
}, [])
// 3. 普通的函数
const increment = () => {
setCount(count +1)
}
return (
<div>
<h2>计数: {count}</h2>
<button onClick={increment}>+1</button>
<HYHome increment={increment}/>
<h2>{ message }</h2>
<button onCick={e => setMessage(Math.random())}>修改message</button>
</div>
)
})