2022即将到来,前端发展非常迅速,现在的前端已经不再是写写简单的页面了,伴随着web大前端,工程化越来越火,也出现了很多框架。很多公司也不单单是使用单一的框架了,作为前端开发国内最火的两大框架,都是必须要掌握的。所以,我决定在这疫情肆虐的年底把React学习一下,也为自己将来找工作多一分竞争力...
学习阶段,如有不对之处,欢迎评论区留言,我及时更正
本文已连载,其它章节传送门⬇️
React Hooks
Hook 是 React 16.8 的新增特性,为函数组件提供状态、生命周期等class组件才有的特性。Hook只能在函数组件中使用,函数组件有了Hook就不再是无状态组件了。
简单介绍下hook,它是一些可以让你在函数组件中“钩入” React state 及生命周期等特性的函数,react内置了一些hook函数,我们也可以自定义自己的hook。需要注意的是,hook函数只能在函数组件内部调用,不能嵌套在普通js函数和if else 以及for循环之中。因为react是按照hook的调用顺序来识别区分每一个hook的,如果调用顺序不同会到导致react不知道是哪个hook
注意:class相关的API 在hooks中不可以使用,函数组件没有生命周期钩子函数,也没有state和this相关用法...
useState
useState Hook
,为函数组件提供状态,并初始化数据,修改数据
import { useState } from 'react'
import ReactDom from 'react-dom'
function Demo() {
/**
useState可以接收函数类型的初始值
const [state, setstate] = useState(() => {return 0})
*/
const [state, setstate] = useState(0)
const handleClick = () => {
setstate(state + 1)
}
return (
<div>
<h1>{state}</h1>
<button onClick={handleClick}>点击</button>
</div>
)
}
ReactDom.render(<Demo />, document.querySelector('#root'))
React中导入useState
并且在函数组件中调用,接收一个初始值参数,参数可以是任意值。返回一个数组,数组的第一位就是传入的初始值,第二位是一个我们用来修改此数据的函数
当我们调用useState
函数第二个返回值来修改组件数据的时候,react将使用新的数据替换旧的数据,并且组件会重新渲染,多次点击调用setstate
修改数据,我们的useState
会拿到上一次的数据,而不是每次渲染都重新初始化。但需要注意的是,引用类型数据的修改,我们要用新的数据去替换掉旧的数据,而不是直接修改旧数据,不然组件不会更新重新渲染,例如我们把代码改下:
...
const [state, setstate] = useState({ name: 'ls' })
...
const handleClick = () => {
state.name = '小明'
setstate(state)
}
可以看到,无论我们怎么点击都无法生效的,因为引用类型是一个地址,如果我们想修改数据,必须用一个新的对象去覆盖旧的对象
useState参数可以写成箭头函数的形式
const [state, setstate] = useState(() => {
console.log('useState')
return {
name: 'ls'
}
})
useEffect
Effect Hook
可以让你在函数组件中执行副作用操作,副作用指操作DOM,获取数据,发布订阅等等... 官网有说明你可以把Effect Hook
看作 componentDidMount
componentDidUpdate
和componentWillUnmount
三个生命周期钩子函数的组合
import ReactDOM from 'react-dom'
import React, { useState, useEffect } from 'react'
export default function Demo() {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count + 1)
}
useEffect(() => {
document.title = `你点击了${count}次`
})
return (
<div>
<p>点击了{count}次</p>
<button style={{
width: '70px', height: '30px',
backgroundColor: '#1E90FF', color: 'white',
outline:'none', border: 'none',
borderRadius: '5px'
}}
onClick={handleClick}
>
点击 +1
</button>
</div>
)
}
ReactDOM.render(<Demo/>, document.querySelector('#root'))
可以看到当点击的时候去修改document的title,这些就属于渲染组件额外的事情,属于副作用代码,当然就需要写到useEffect
里面喽~effect 会在组件渲染后以及组件更新后执行
useEffect的依赖项
依赖项就是能够设置useEffect的依赖,只在count变化时,才会执行相应的effect。比如我们修改下代码:
const [money, setMoney] = useState(100)
const changeMoney = () => {
setMoney(
money + 10
)
}
return (
<div>
<h1>Count:{count}</h1>
<h1>Money:{money}</h1>
<button onClick={handleClick}>修改count</button>
<button onClick={changeMoney}>修改Money</button>
</div>
)
可以看到,首次渲染执行一次,当我们点击修改count和修改Money的时候都执行了一次。 但是当我们点击修改Money的时候其实并不需要去重新执行effect的 因为count并没有改变,这时候我们就需要为effect添加一个依赖
useEffect(() => {
console.log('useEffect副作用执行了')
document.title = `当前已点击${count}次`
},[count])
useEffect接收第二个为数组的参数,里面是我们effect依赖到的数据,此时我们再点击想修改Money的时候 就不会再执行effect函数了,这样就跳过了不必要的执行
我们还可以把依赖项设置为一个空数组,这样该effect只会在 组件第一次渲染的时候执行,此时可以看作是componentDidMount
钩子函数
useEffect清理副作用
清理副作用就是当组件销毁的时候,我们需要清除的一些操作,比如取消监听事件,定时器等等...
useEffect(() => {
const timer = setInterval(() => {
console.log('定时器执行了')
})
return () => { Window.clearInterval(timer) }
})
useEffect
还可以实现componentWillUnmount
钩子函数的作用,通过return一个函数来实现,此函数会在每次useEffect
的回调函数执行之前执行一次,目的是为了清除上一次副作用带来的影响和组件销毁的时候执行
useContext
useContext Hook
能够订阅context的值和读取context的变化,相当于 class 组件中的 static contextType = MyContext 或者 <MyContext.Consumer> 。这样我们在函数组件中也能够使用context共享数据了
import React, { useContext } from 'react'
import ReactDOM from 'react-dom'
const obj = {
name: '小明',
age: 18
}
const userContext = React.createContext(obj)
function Son() {
const user = useContext(userContext)
return (
<div>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
</div>
)
}
function Person() {
return (
<div>
<Son/>
</div>
)
}
function App() {
return (
<div>
<userContext.Provider value={{name: '小红', age: 20}}>
<Person/>
</userContext.Provider>
</div>
)
}
ReactDOM.render(<App/>, document.querySelector('#root'))
创建三个组件,并创建context共享数据,我们需要在子组件Son里面使用useContext
读取离它自身最近的context值,此时就需要在Son组件里用到此方法。该Hook会伴随离它最近的Provider的value值变化而重新触发,官方也有说明:调用了 useContext
的组件总会在 context 值变化时重新渲染 传送门
自定义 Hook
除了React给我们提供的一些内部的hook,我们还可以实现自己的hook。当我们有一段逻辑在多个组件中都使用到的时候,我们就可以提取成一个单独的自定义hook,这样只需在用到的组件中引入而无需每个组件都写一遍
export default function Index() {
const [position, setPosition] = useState({
x: 0,
y: 0
})
useEffect(() => {
const move = (e) => {
setPosition({
x: e.pageX,
y: e.pageY
})
}
document.addEventListener('mousemove',move)
return () => {
document.removeEventListener('mousemove', move)
}
},[])
return (
<div>
<p>x轴:{position.x}</p>
<p>y轴:{position.y}</p>
</div>
)
}
例如我们有一段获取坐标轴的逻辑,在多个组件中都需要使用这个坐标轴,那么我们就可以把这段逻辑抽取为一个单独的hook.js文件中去
新建hook.js
// 新建hook.js
import {useState, useEffect} from 'react'
export function useMove() {
const [position, setPosition] = useState({
x: 0,
y: 0
})
useEffect(() => {
const move = (e) => {
setPosition({
x: e.pageX,
y: e.pageY
})
}
document.addEventListener('mousemove',move)
return () => {
document.removeEventListener('mousemove', move)
}
},[])
return position
}
在组件中引入此hook,修改代码:
import ReactDOM from 'react-dom'
import React from 'react'
import {useMove} from './hook'
export default function Index() {
const { x, y } = useMove()
return (
<div>
<p>x轴:{x}</p>
<p>y轴:{y}</p>
</div>
)
}
ReactDOM.render(<Index/>, document.querySelector('#root'))
这样我们在任意一个组件都可以引入此hook,但是需要注意的是 自定义hook名必须是和react内部hook一样的 采用小驼峰命名规则