理解React Hook(基础的)

函数组件

在v16.8 版本之前,react组件的标准写法是class(类),而在版本之后,react引入了一种全新的api,叫做 React Hooks,它颠覆了以前的用法。

import ReactDOM from 'react-dom';
import React,{Component} from 'react'

class ComponentA extends Component{
  constructor(props){
    super(props)
    this.state={
      id:0
    }
    this.changeBind = this.changeBind.bind(this)
  }
  changeBind(){
      this.setState({
        id:this.state.id + 1
      })
  }
  render(){
    let id = this.state.id
    return(<div>
      <button onClick={()=>this.changeBind()}>修改id</button>
      <ComponentB id={id}></ComponentB>
    </div>)
  }
}


ReactDOM.render(<ComponentA />,document.getElementById('root'))

在react固有class(类)写法的影响下,一个普通的组件基本会具备其上的内容,大部分人看来,仅这一个简单组件的代码量都很重,更不用讲一个完成的react项目多层级嵌套了。

为了解决这个问题,其实react团队在很早之前就已经支持函数组件了,但是放在现在的我们,函数组件的用法显然在项目中会受到很大的限制,它没有生命周期,也无法管理状态

function ComponentB(props){
  let id = props.id
  function changeID(){
    id += 10;
    console.log('ComponentB:',id)
  }
  return(<h3>{id}<button onClick={()=>changeID()}>修改本组件的id</button></h3>)
}

如上一例,最开始的id依赖于父组件,当点击父组件的按钮时,id发生变化,组件ComponentB视图中的id也随之发生了变化,但在函数组件内部,我定义了方法,当点击ComponentB中的按钮时,虽然触发了事件,但页面视图却没有发生改变,这就是没有状态管理的函数组件的局限。

在这里插入图片描述

什么是Hook?

为了解决以上问题,react提供了react hook,可以说它是一种专门辅助函数组件的工具,本身也是一个特殊的函数,它可以让你“钩入” React 的特性。

它提供了四钟常用的钩子

useState()

useContext()

useEffect()

1、useState()

import ReactDOM from 'react-dom';
import React,{Component,useState} from 'react'

class ComponentA extends Component{...}

function ComponentB(props){
  let [id,changeID]=useState(props.id)
  return(<h3>{id}<button onClick={()=>changeID(id+10)}>修改本组件的id</button></h3>)
}

ReactDOM.render(<ComponentA />,document.getElementById('root'))

我们在组件中引入useState,并将ComponentB稍作修改,现在的我们就可以单独控制ComponentB中的id了,当我们点击按钮,页面视图中的id也随之发生改变。

由于纯函数组件中不能有状态,所以它把状态放在了状态钩子useState中

解读 let [id,changeID]=useState(props.id)

在使用钩子的时候,它会返回一个数组,数组中固有两个参数,一个是我们状态控制的变量 = 》id,另一个是用来控制状态的函数=》changeID,而我们在使用状态钩子的时候传入了一个初始值 = 》props.id,这个初始值将赋值给状态控制的变量=》id

也就是说,这个状态钩子帮我们做了两件事,一是声明了一个需要被控制的字段,并给了它一个初始值,二是定义了一个可修改状态的公用方法,并将其封装进了我们声明的函数方法中。

注意:当前组件ComponentB中的id是重新定义的,当我们修改了ComponentB中的id后,父组件中的id并不受其影响,且后续若父组件的id发生了改变,ComponentB的id也不受其影响。

在这里插入图片描述
2、useContext()与createContext()

从上边的例子我们知道,useState只适用单个函数组件的状态控制,如果这时候我们有两个函数组件公用同一个变量,且当该变量发生改变时两个函数组件都会受其影响,这时候,我们就需要使用到 共享状态钩子useContext()

为了适用我们当前内容,下面以新的实例进行说明:

import ReactDOM from 'react-dom';
import React,{createContext,useContext} from 'react'
const CommonContext = createContext({});
class App extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      id:0
    }
  }
  render(){
    let id = this.state.id
    return(
      <div>
        父组件:<button onClick={()=>{this.setState({id:id + 1})}}>修改id</button>
        <CommonContext.Provider value={{id:id}}>
          <ComponentA />
          <ComponentB />
      </CommonContext.Provider>
      </div>
    )
  }
}

function ComponentA(props) {
  let {id} = useContext(CommonContext)
  return(<div>子组件A{id}</div>)
}

function ComponentB(props) {
  let {id} = useContext(CommonContext)
  return(<div>子组件B{id}</div>)
}

ReactDOM.render(<App />,document.getElementById('root'))

当前实例中,如果你点击了父组件中的按钮,对id进行修改,两个子组件A、B中依赖的id都会随之发生改变。

初次接触hook,可能看到代码中的 CommonContext.Provider 会有疑惑,不要着急,马上为你解惑:
在这里插入图片描述
通俗点说,你修好了一套房,取名叫CommonContext,Provider就是房子的墙壁,它区分了房子的内外,只有在房子里的人才能使用空调,洗衣机,电视机,房子外就没办法使用,当然,也要你先有空调、电视、洗衣机才有得用,value就是你要放这些家电的空间,在value中就是你要放置的家电。

需要共享某个参数的组件都需要放置在 CommonContext.Provider 组件内,只有放置在这个组件内的组件才可以共享它的参数,value中可以定义你要共享的字段,在子函数组件中,引入一下共享钩子(useContext(CommonContext))就可以使用你定义的参数了。

在这里插入图片描述
点击父组件的按钮,两个子组件所使用的id都会受其影响;

3、useEffect 副作用钩子

它跟 class 组件中的 componentDidMountcomponentDidUpdatecomponentWillUnmount 具有相同的用途,当你在 React 组件中执行数据获取、订阅或者手动修改 DOM ,就会触发副作用钩子,react 也将这些操作称作 “副作用”。

以下我们继续使用新的实例进行说明:

import ReactDOM from 'react-dom';
import React,{useState,useEffect } from 'react'
function App() {
  const [id,setId] = useState(0)
  useEffect(()=>{
    alert('触发了副作用钩子')
  })
  return (
  <div>ID:{id}<button onClick={()=>setId(id+1)}>修改id</button></div>
  )
}
ReactDOM.render(<App />,document.getElementById('root'))

以上是一个简单的副作用钩子使用方法,默认情况下,每一次渲染都会触发副作用钩子,包括第一次

在这里插入图片描述
因为刷新页面的时候组件进行了第一次渲染,而点击按钮的时候又对数据进行了操作,所以都触发了副作用钩子

副作用钩子还可以在组件中多次使用。

通过跳过 Effect 的 useEffect 第二个参数

综上实例,我们知道了Effect的第一个参数是一个函数,当每次进行副作用操作之后,Effect都会执行这个函数,而在某些情况下,每次渲染后都执行副作用钩子可能会导致性能问题,这时候就有了Effect的第二个参数,它规定了只有在第二个参数中的变量发生改变时,才会执行当前这个副作用钩子。

import ReactDOM from 'react-dom';
import React,{useState,useEffect } from 'react'

function App() {
  const [id, setID] = useState(0);
  let [ids,changeIDs] = useState(12);
  useEffect(()=>{
    console.log('触发第一次副作用钩子',id)
  })
  useEffect(()=>{
    console.log('触发第二次钩子')
  },[ids])
  return (
  <div>ID:{id}<button onClick={()=>setID(id+1)}>修改id</button></div>
  )
}


ReactDOM.render(<App />,document.getElementById('root'))

在这里插入图片描述
可以发现,除了初始化的时候调用了一次第二个副作用钩子,再后续的id发生改变时,ids这个变量并没有发生变化,所以不会触发第二个副作用钩子。

这就是useEffect 第二个参数的作用,且有心的你可能会觉得疑惑,这副作用钩子的第二个参数是一个数组,那意思是不是可以放置多个变量进行控制,事实也是如此:

我们对实例进行简单的修改:

import ReactDOM from 'react-dom';
import React,{useState,useEffect } from 'react'

function App() {
  const [id, setID] = useState(0);
  let [ids,changeIDs] = useState(12);
  let [needChange,changeNeedChange] = useState(0);
  useEffect(()=>{
    console.log('触发第一次副作用钩子',id)
  })
  useEffect(()=>{
    console.log('触发第二次钩子',needChange)
  },[ids,needChange])
  setTimeout(()=>{changeNeedChange(20)},1000)
  return (
  <div>ID:{id}<button onClick={()=>setID(id+1)}>修改id</button></div>
  )
}


ReactDOM.render(<App />,document.getElementById('root'))

在组件初始化后的一秒,执行了一次修改needChange变量:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值