React常用Hook、自定义Hook

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正确,推荐看下这篇文章juejin.im/post/5c982774,大概意思就是每个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不仅可以这么用,还能替换掉高阶组件。

总结完毕,撒花,欢迎更正。

发布于 2019-12-24 09:53