React常用Hook、自定义Hook
常用Hook:
useState:
在以前的react项目中,如果某个组件需要有数据状态,就需要创建class组件,例如:
import React from 'react'
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((state) => {
return {count: state.count + 1}
})
}
render() {
return (
<button onClick={this.handleClick}>已点击{this.state.count}</button>
)
}
}
而现在有了hook,也能在function组件中管理数据状态了,上面的例子用function实现。
import React, { useState } from 'react'
const Counter = () => {
// 定义需要管理状态的数据,在useState()中设置默认值
const [count, setCount] = useState(0)
return (
<button onClick={() => {setCount(count + 1)}}>已点击{count}</button>
)
}
感觉使用更加的方便了,不用再因为需要管理数据状态的原因将function组件换成class组件了。
注意:
class组件中,state更新数据的方式是合并,也就是说只修改了我们要修改的那一项,例如:
constructor(props) {
super(props);
this.state = { count: 0, name: 'frank' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState((state) => {
return {count: state.count + 1}
})
}
// 运行一次后的state结果是{ count: 1, name: 'frank' }
但在useState中,更新数据的方式是替换
const [someOne, setSomeOne] = useState({ count: 0, name: 'frank' })
const handleClick = () => {
setSomeOne({ count: someOne.count + 1 })
}
// 此时someOne的结果是{count: 1}
useEffect:
在的class组件中,有生命周期函数,常用的有componentDidMount和componentDidUpdate,这里就不做例子了,现在可以在function组件中使用useEffect替代上面的方法了。
useEffect会在每次页面完成渲染或更新数据后执行。
import React, { useState, useEffect } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
useEffect(() => {
document.title = '已点击' + count + '次'
})
return (
<button onClick={() => {setCount(count + 1)}}>已点击{count}次</button>
)
}
// 此时title会随用户的点击而改变
useEffect还有第二个参数,通过监听传入的参数来决定是否执行这个useEffect。
import React, { useState, useEffect } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
useEffect(() => {
console.log('count改变了')
},[count])
return (
<div>
<button onClick={() => {setCount(count + 1)}}>已点击{count}次</button>
<button onClick={() => {setNum(num + 1)}}>已点击{num}次</button>
</div>
)
}
// 此时只有第一个按钮被点击时,控制台才会打印出count改变了
如果你希望只在页面渲染完成的时候执行(数据更新后不执行),你可以将第二个参数设置成[],这样useEffect就不依赖任何值了,只会在页面渲染完成后执行。
以上的useEffect都是不用清除的useEffect,但有些情况下我们还是需要清除useEffect的,例如为某个元素添加事件:
const Counter = () => {
const [count, setCount] = useState(0)
useEffect(() => {
// 在页面更新后添加事件
const updateMouse = (e) => {
console.log('ok')
setCount(count + 1)
}
document.addEventListener('click', updateMouse)
})
return (
<div>{count}</div>
)
}
// 表面上你看到的页面没什么问题,但是你可以打开控制台看下每点击一次ok的打印次数。
// 应该是1 3 6 12
这是因为原先的事件没有被清除,一直存在,至于为什么它的count正确,推荐看下这篇文章https://juejin.im/post/5c9827745188250ff85afe50,大概意思就是每个render都有自己的一个state值,互不影响,所以,我们看到的count值是正确的。这种情况下,我们应该清除useEffect。
const Counter = () => {
const [count, setCount] = useState(0)
useEffect(() => {
// 在页面更新后添加事件
const add = (e) => {
console.log('ok')
setCount(count + 1)
}
document.addEventListener('click', add)
return () => {
document.removeEventListener('click', add)
}
})
return (
<div>{count}</div>
)
}
此时,再去看控制台ok的次数就会正常显示,useEffect里的return会在每次新的useEffect执行之前执行,这就做到了清除useEffect了。
自定义Hook
现在有这样一种需求,我们需要获取鼠标当前的位置坐标,但是不仅仅是一个页面需要,此时我们就可以用到自定义Hook了。
import React, { useState, useEffect } from 'react'
const useMousePosition = () => {
const [position, setPosition] = useState({x: 0, y: 0 })
useEffect(() => {
const updateMouse = (e) => {
setPosition({ x: e.clientX, y: e.clientY })
}
document.addEventListener('mousemove', updateMouse)
return () => {
document.removeEventListener('mousemove', updateMouse)
}
})
return position
}
export default useMousePosition
之后在需要的地方引入然后调用即可
import React from 'react'
import useMousePosition from './useMousePosition'
function App() {
const position = useMousePosition()
return (
<div>
<div>x: {position.x}</div>
<div>y: {position.y}</div>
</div>
)
}
当然,自定义hook不仅可以这么用,还能替换掉高阶组件。
总结完毕,撒花,欢迎更正。