Hooks 在函数组件中使用 state 以及其他的 React 特性 (State Hook 、Effect Hook 、 Ref Hook)

Hooks

Hooks优势

  • 方便复用状态逻辑
  • 副作用的专注点分离
  • 函数组件无this问题

使用函数组件也有以下几点好处:

  • 风格清爽,函数式编程风格,函数式组件、状态保存在运行环境、每个功能都包裹在函数中,整体风格更清爽,更优雅。
  • 代码量小
  • 可以使用函数组合,嵌套,实现功能更加强大的组件。
  • 组件不会被实例化,整体渲染性能得到提升。

1. React Hook/Hooks是什么?

(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性

2. 三个常用的Hook

(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()

3. State Hook

(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)  
(3). useState()说明:
        参数: 第一次初始化指定的值在内部作缓存
        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
案例
import React from 'react'

// class Demo extends React.Component {
//   state = { count: 0 }
//   add = () => {
//     // 对象式的setState
//     this.setState({ count:  this.state.count + 1 })
//   }
//   render() {
//     return (
//       <div>
//         <h1>当前求和为:{this.state.count}</h1>
//         <button onClick={ this.add }>点我+1</button>
//       </div>
//     )
//   }
// }
// 初始化的调用顺序不能被打乱 
  // useState 只能在顶层调用,不饿能在条件语句中调用,也不能在循环块中调用
  // 调用的次数不能多也不能少
function Demo() {
  const [Count, setCount] = React.useState(0)
  const [Name, setName] = React.useState("zhangsan")
  function add(){
    setCount(Count+1)
  }
  function changeName(){
    setName("set")
  }
  return (
    <div>
      <h1>当前求和为:{Count}</h1>
      <h1>当前的名字为:{Name}</h1>
      <button onClick={ add }>点我+1</button>
      <button onClick={ changeName }>切换名字</button>
    </div>
  )
}
export default Demo

4. Effect Hook

(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
(3). 语法和说明: 
        useEffect(() => { 
          // 在此可以执行任何带副作用操作
          return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
          }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
(4). 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
    	componentWillUnmount() 
案例

案例1

import React from 'react'
import ReactDOM from 'react-dom'

// class Demo extends React.Component {
//   state = { count: 0 }
//   add = () => {
//     // 对象式的setState
//     this.setState({ count:  this.state.count + 1 })
//   }
//   componentDidMount() {
//     setInterval(() => {
//       this.setState(state => ({count:state.count+1}))
//     },1000)
//   }
//   unmount = () => {
//     ReactDOM.unmountComponentAtNode(document.getElementById('root'))
//   }

//   componentDidMount(){
//     this.timer= setInterval(() => {
//       this.setState(state => ({count:state.count+1}))
//     },1000)
//   }
//   componentWillUnmount() {
//     clearInterval(this.timer)
//   }
//   render() {
//     return (
//       <div>
//         <h1>当前求和为:{this.state.count}</h1>
//         <button onClick={this.add}>点我+1</button>
//         <button onClick={this.unmount}>点我卸载组件</button>
//       </div>
//     )
//   }
// }
function Demo() {
  const [Count, setCount] = React.useState(0)
  function add(){
    setCount(Count+1)
  }
  function unmount() {
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  React.useEffect(() => {
    let timer= setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
    return () => {
      clearInterval(timer)
    }
  },[])
  return (
    <div>
      <h1>当前求和为:{Count}</h1>
      <button onClick={add}>点我+1</button>
      <button onClick={unmount}>点我卸载组件</button>
    </div>
  )
}
export default Demo

案例2

// import React, { useState } from 'react'
import React,{Component,useState,useEffect} from 'react'
// class App2 extends Component {
//   state = {
//     count: 0,
//     size: {
//       width: document.documentElement.clientWidth,
//       heigth: document.documentElement.clientHeight
//     }
//   }
//   onResize = () => {
//     this.setState({
//       size: {
//         width: document.documentElement.clientWidth,
//         heigth: document.documentElement.clientHeight
//       }
//     })
//   }
//   componentDidMount() {
//     document.title = this.state.count
//     window.addEventListener('resize', this.onResize, false)
//   }
//   componentWillUnmount() {
//     window.removeEventListener('resize', this.onResize, false)
//   }
//   componentDidUpdate() {
//     document.title = this.state.count;
//   }
//   render() {
//     const { count,size } = this.state
//     return (
//       <button
//         type="button"
//         onClick={()=>{this.setState({count:count+1})}}
//       >
//         Click ({count})
//         size:{size.width}x{size.heigth}
//       </button>
//     )
//   }
// }
function App(props) {
  // 延迟初始化 (只调用一次)
  const [count, setCount] = useState(0);
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    heigth: document.documentElement.clientHeight
  })
  // 初始化的调用顺序不能被打乱 
  // useState 只能在顶层调用,不饿能在条件语句中调用,也不能在循环块中调用
  // 调用的次数不能多也不能少
  const onResize = () => {
    setSize({
      width: document.documentElement.clientWidth,
      heigth: document.documentElement.clientHeight
    })
  }
  useEffect(() => {
    document.title = count
  })
  useEffect(() => {
    window.addEventListener('resize', onResize, false)
    return () => {
      window.removeEventListener('resize', onResize, false)
    }
  },[])
  return (
    <button
      type="button"
      onClick={()=>{setCount(count+1)}}
    >
      Click ({count})
      size:{size.width}x{size.heigth}
    </button>
  )
}

export default App;

5. Ref Hook

(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
案例
import React from 'react'
import ReactDOM from 'react-dom'

// class Demo extends React.Component {
//   state = { count: 0 }
//   myRef =React.createRef()
//   add = () => {
//     // 对象式的setState
//     this.setState({ count:  this.state.count + 1 })
//   }
//   componentDidMount() {
//     setInterval(() => {
//       this.setState(state => ({count:state.count+1}))
//     },1000)
//   }
//   unmount = () => {
//     ReactDOM.unmountComponentAtNode(document.getElementById('root'))
//   }
//   show = () => {
//     alert(this.myRef.current.value)
//   }

//   componentDidMount(){
//     this.timer= setInterval(() => {
//       this.setState(state => ({count:state.count+1}))
//     },1000)
//   }
//   componentWillUnmount() {
//     clearInterval(this.timer)
//   }
//   render() {
//     return (
//       <div>
//         <input type="text" ref={this.myRef}/>
//         <h1>当前求和为:{this.state.count}</h1>
//         <button onClick={this.add}>点我+1</button>
//         <button onClick={this.unmount}>点我卸载组件</button>
//         <button onClick={this.show}>点击提示数据</button>
//       </div>
//     )
//   }
// }
function Demo() {
  const [Count, setCount] = React.useState(0)
  const myRef =React.useRef()
  function add(){
    setCount(Count+1)
  }
  function unmount() {
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  function show() {
    alert (myRef.current.value)
  }
  React.useEffect(() => {
    let timer= setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
    return () => {
      clearInterval(timer)
    }
  },[])
  return (
    <div>
      <input type="text" ref={myRef}/>
      <h1>当前求和为:{Count}</h1>
      <button onClick={add}>点我+1</button>
      <button onClick={unmount}>点我卸载组件</button>
      <button onClick={show}>点我提示数据</button>
    </div>
  )
}
export default Demo

import React,{PureComponent,useEffect, useState,useCallback ,useMemo,useRef} from 'react'

class Counter extends PureComponent {
  speak() {
    console.log( `now counter is: ${this.props.count}`)
  }
  render() {
  const {props} = this
    return (
      <h1 onClick={props.onClick}></h1>
    )
  }
}
function App(props) {
  const [count, setCount] = useState(0);
  const it =useRef()
const counterRef = useRef()
// 会执行两次 一次等于3 一次从三改成其他数字时
  const double = useMemo(() => {
    return count*2
  }, [count === 3])
  
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count=>count+1)
    },1000)
  }, [])
  
  useEffect(() => {
    if (count >= 10) {
      clearInterval(it.current)
    }
  })

  const onClick = useCallback(() => {
    counterRef.current.speak()
  },[counterRef])
  return (
    <div>
      <button
        type="button"
        onClick={()=>{setCount(count+1)}}
      >
        Click ({count})
        double:  ({double})
      </button>
      <Counter ref= {counterRef} count={double} onClick={onClick}/>
    </div>
  )
}

export default App;

6.自定义Hooks

案例
import React,{PureComponent,useEffect, useState,useCallback ,useMemo,useRef} from 'react'



function useCounter(count) {
   const size = useSize()
    return (
      <h1>{count},{size.width}{size.height }</h1>
  )
}

// 自定义use参数
function useCount(defaultCount) {
  const [count, setCount] = useState(defaultCount);
  const it =useRef()
  
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count=>count+1)
    },1000)
  }, [])
  
  useEffect(() => {
    if (count >= 10) {
      clearInterval(it.current)
    }
  })
  return [count,setCount]
}

function useSize() {
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height:document.documentElement.clientHeight
  })

  const onResize = useCallback(() => {
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    })
  },[])

  useEffect(() => {
    window.addEventListener('resize', onResize, false)
    return () => {
      window.removeEventListener('resize',onResize,false)
    }
  }, [])
  return size
}

function App(props) {
  const [count, setCount] = useCount(0)
  const Counter = useCounter(count)
  const size = useSize()
  return (
    <div>
      <button
        type="button"
        onClick={()=>{setCount(count+1)}}
      >
        Click ({count}),{size.width}x{size.height}
      </button>
      {Counter}
    </div>
  )
}

export default App;


版权声明:本文为weixin_50001396原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。