• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

react笔记_06函数组件

武飞扬头像
乖女子@@@
帮助1

组件

什么叫做组件?

用来实现局部功能效果的代码和资源的集合叫做组件(html/css/js/image等)

分类

在react中组件分为函数式组件与类式组件;

今天主要了解的是函数式组件~

函数组件语法

函数组件和普通函数类似,只是函数的返回值为vdom

function 组件名(){
  return vdom
}

函数组件渲染

  • [1]查看组件首字母大/小写:

    若是首字母小写则默认为html标签,会去寻找对应的html标签并渲染,若是没有找到对应的html标签则会报错;

    若是首字母大写则默认为组件,会去寻找对应的组件并渲染,若是没有找到对应的组件就会报错;

    tips: 组件的组件名 首字母必须 大写,否则会被认作html标签

  • [2]开始渲染(此处以函数组件为例)

    react会直接调用该函数得到对应的虚拟dom进行渲染。

函数组件的this指向

在渲染组件时若是发现组件是函数式组件时就会直接调用该函数, 得到对应的vdom并渲染。因此函数式组件内部的this指向的是undefined,也就是说函数式组件内部无法使用this;

tips:在非严格模式下,直接调用函数,函数内部的this指向的是Window;在严格模式下,直接调用函数,函数内部的this指向的是undefined。
jsx语法进行编译时,会先通过babel进行编译;babel进行编译时使用的是严格模式,因此jsx语法下函数组件中的this指向的是undefined!

函数组件的hook

什么是hook?

以前我们称函数组件为简单组件,因为函数组件是无状态的(没有state)。

而在React 16.8版本增加了 Hook,它可以让你在不编写 class 组件的情况下,也就是我们可以在函数组件中使用 state 以及其他的 React 特性。

Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

hook使用规则

  • 不要在循环,条件或嵌套函数中调用 Hook,确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。
  • 只在 React 函数中调用 Hook,不要在普通的 JavaScript 函数中调用 Hook。

useState

useState 是我们要学习的第一个 “Hook”。

举例说明

学新通
现在有如上需求:一个定时器 当点击 “点我num ” 按钮时,将num的值 1。

若是我们使用class组件如下

class App extends React.Component{
  state={
    num:0
  }

  render(){
    const { num } = this.state
    return(
      <div>
        { num }
        <button onClick={this.add}>点我num  </button>
      </div>
    )
  }

  // 箭头函数,否则this指向为undefined
  add=()=>{
    // 第一个参数为函数,因为当前修改值依赖于上一次的值
    this.setState(prestate=>({num: prestate.num 1}))
  }
}

而在函数组件中,我们没有 this,所以我们不能分配或读取 this.state。我们直接在组件中调用 useState Hook.

import React, { useState } from 'react'
export default function New2 (){
 const [num, setNum] = useState(0) // 初始化数据
 const editNum = () =>{
   setNum(num 1) // 更新数据
 }  
 return(
   <div>
     {num}
     <button onClick={editNum}>点我num  </button>
   </div>
 )
}
语法
1. 定义一个 state变量
  useState(initialState)

通过useState函数初始化数据

  • 参数:initialState为初始 state 值,不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。

  • 返回值:返回值为一个数组,数组中存在两个元素,第一个元素为当前state,第二个元素为更新此state的函数。

  • 该函数的作用是 定义一个 “state 变量”。这是一种在函数调用时保存变量的方式 。一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留

    import {useState} from 'react'
    function App(){
      const [num1, setNum1] = useState(0)
      let num2 = 0
      return (
        <div>
          { num1 }
          <br />
          { num2 }
          <br />
          <button onClick={add}>点击修改num</button>
        </div>
      )
      function add(){
        num2 = num2  
        setNum1(prenum1=>prenum1 1)
        console.log('num2', num2) // 永远都是0
      }
    }
    

    在上述案例中,每次点击按钮都会通过useState去修改数据(触发组件更新)。每次更新组件时普通变量num2会被赋值为0,而state中的变量num1的值会被react保留,因此每次点击按钮num1 ,num2=0

2. 读取state变量
const [num] = this.useState(0)

console.log(num) // 使用时直接当作变量使用即可
3. 更新state变量值
const [num, numState] = this.useState(0)

// numState是用于更新的函数
numState(value) // value可以为一个想要更新的值也可以是一个函数

// 传入一个值 -> 该值作为更新值去进行更新
newState(1) // 会将num值变为1

// 传入一个函数 ->  会自动调用该函数并将上一次的state值作为参数传入
newState(prestate => return prestate 1 ) // 会将num的值在当前基础上加1

如果新的 state 需要通过使用先前的 state 计算得出,对于同步更新来说 传值或者是函数是没有区别的,但是对于异步更新来说更推荐使用 函数,使用以下例子进行说明:

  • 参数为一个值

    import React, { useState } from 'react'
    export default function New2 (){
      const [num, setNum] = useState(0)
      const editNum = () =>{
        setTimeout(()=>{
          setNum(num 1)
        },1000)
      }  
      return(
        <div>
          {num}
          <button onClick={editNum}>点我num  </button>
        </div>
      )
    }
    
  • 参数为一个函数

    import React, { useState } from 'react'
    export default function New2 (){
      const [num, setNum] = useState(0)
      const editNum = () =>{
        setTimeout(()=>{
          setNum(preState=> preState 1 )
        },1000)
      }  
      return(
        <div>
          {num}
          <button onClick={editNum}>点我num  </button>
        </div>
      )
    }
    

    当我设置为异步更新,点击按钮延迟到1s之后去更新数据,当我快速点击按钮时,也就是说在1s多次去触发更新,但是只有一次生效,因为 count 的值是没有变化的。

    当使用函数式更新 state 的时候,这种问题就没有了,因为它可以获取之前的 state 值,也就是代码中的 preState 每次都是最新的值

useEffect

Effect Hook 可以让你在函数组件中执行副作用操作。

语法
import React, { useEffect } from 'react'
useEffect(()=>{
  // 逻辑代码
},[])
  • 第一个参数是一个函数
  • 第二个参数为一个数组(可省略)
执行时机
Every time your component renders, React will update the screen and then run the
code inside useEffect.

默认情况下 useEffect在每次render之后执行
若是不想每次render之后执行,可以接收第二个参数来控制跳过执行。

跳过 Effect
[1] 每次render之后执行

若是第二个参数不传,则useEffect将每次render之后都会执行,相当于omponentDidMount,componentDidUpdate两个生命周期函数。

useEffect(()=>{
  console.log('执行了')
})

上述代码在 每次render之后都会执行。

[2] 仅在挂载时执行

若是第二个参数传入一个空数组,则useEffect将仅在初始化时执行,相当于omponentDidMount。

useEffect(()=>{
  console.log('执行了')
},[])

上述代码仅仅在初始化时执行。

[3]仅当某个值变化时执行

若是第二个参数传入 带有元素的数组,则useEffect将仅在初始化元素的值发生变化时执行

useEffect(()=>{
  console.log('执行了')
},[num])

上述代码仅在初始化和num的值发生变化时执行。

useEffect(()=>{
  console.log('执行了')
},[num,value])

上述代码仅在初始化和 num或value值发生变化时执行。

注意
若是(第二个参数)数组元素中存在引用类型,就应该在第一个参数也就是执行的副作用函数中不要用setState相关的代码,否则容易引起死循环!

[4] 在组件卸载时执行

在useEffect的第一个参数中,若是存在return,则return的函数将会在组件卸载时执行

举例说明

useEffect(() => {
  window.a = 100;
  return ()=>{window.a = 0};
}, []);

上述代码在组件挂载时执行 代码window.a = 100; 在组件卸载时执行window.a = 0

现象- useEffect在初始化时会执行两次

在写项目时,发现初次渲染useEffect执行了两次,在网上搜了一下发现这是React18 新加的特性

  • 这是 React18 才新增的特性。
  • 仅在开发模式(“development”)下,且使用了严格模式(“Strict Mode”)下会触发; 生产环境(“production”)模式下和原来一样,仅执行一次。
  • 之所以执行两次,是为了模拟立即卸载组件和重新挂载组件。为了帮助开发者提前发现重复挂载可能会造成的 Bug。 同时,也是为了以后 React的新功能做铺垫 -> 未来会给 React 增加一个特性,允许 React 在保留状态的同时,能够做到仅仅对UI部分的添加和删除.让开发者能够提前习惯和适应,做到组件的卸载和重新挂载之后, 重复执行 useEffect的时候不会影响应用正常运行。
清除effect

有时候,我们只想在 React 更新 DOM 之后运行一些额外的代码。比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。因为我们在执行完这些操作之后,就可以忽略他们了。还有一些副作用是需要清除的。例如订阅外部数据源。这种情况下,清除工作是非常重要的,可以防止引起内存泄露!

举例说明-清除事件监听

useEffect(() => {
  function handleScroll(e) {
    console.log(e.clientX, e.clientY);
  }
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, []);

useRef

语法
const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象,该对象就是一个普通的Javascript 对象。其 current 属性被初始化为传入的参数(initialValue),这和自建一个{current: initialValue}对象的唯一区别是 useRef 会在每次渲染时返回同一个 ref 对象 -> 引用地址相同

返回的 ref 对象在组件的整个生命周期内持续存在。

与createRef的区别
  • 使用useRef创建一个对象
      const u_refContainer = useRef(initialValue);
    
  • 使用createRef创建一个对象
      const c_refContainer = createRef(initialValue);
    
  • 两者看起来很相似,区别是createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用;

    个人观点➡️:创建一个普通对象{current:initialValue}与使用createRef创建一个对象没什么区别,createRef的使用场景不多。

示例

  • 需求
    在组件中有数据count,现在使用 createRef与useRef分别创建容器,若是容器为空就将count的值赋给容器的current属性。

  • 实现

    import {useState, createRef, useRef} from 'react'
    function App(){
      const [count, setCount] = useState(1)
      const c_saveCount = createRef()
      const u_saveCount = useRef()
      const saveCount = {
        current:''
      }
      if(!c_saveCount.current){
        c_saveCount.current = count
      }
      if(!u_saveCount.current){
        u_saveCount.current = count
      }
      if(!saveCount.current){
        saveCount.current = count
      }
      return (
        <ul>
          <li>当前current: {count}</li>
          <li>createRef存储的count: {c_saveCount.current}</li>
          <li>useRef存储的count: {u_saveCount.current}</li>
          <li>普通对象存储的count: {saveCount.current}</li>
          <li>
            <button onClick={()=>{setCount(precount => precount 1)}}>点我修改count</button>
          </li>
        </ul>
      )
    }
    
  • 结果
    学新通

  • 执行逻辑

    • 初始化
      • 创建变量 count 并赋值为1
      • 通过 createRef 创建容器 c_saveCount -> 此时c_saveCount.current为null ,将 count的值赋给 current属性 -> 1
      • 通过 useRef 创建容器 u_saveCount -> 此时u_saveCount.current为null ,将 count的值赋给 current属性 -> 1
      • 1、1、1
    • 点击按钮 -> 更新(更新时会重新调用函数)
      • state中的变量会被保留 -> 1 -> 2
      • 通过 createRef 创建的容器返回的是新的引用 -> 因此此时c_saveCount.current为null -> 将count的值赋给 current属性 -> 2
      • 通过 useRef 创建的容器返回的是相同的引用 -> 因此此时u_saveCount.current为1
      • 2、2、1
存储数据

useRef既可以存储dom元素也可以存储其他数据 -> 它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式

存储dom
import { useRef } from 'react'
export default function New2 (){
 const inputDom = useRef()
 const getdom = ()=>{
   console.log(inputDom.current) // 得到的就是input dom元素
 }
 return(
   <div>
     <button onClick={getdom}>点我获取dom元素</button>
     <input type="text" ref={inputDom} />
   </div>
 )
}
存储表单数据
function Mycomponent(){
   const formValue = React.useRef({
       keyword:'222',
       page:1,
       size:10
   })
   const valuechange = (e) => {
     formValue.current.keyword = e.target.value
   }
   return (
     <ul>
       <li><input defaultValue='222' onChange={valuechange} /></li>
     </ul>
   )
 }
 ReactDOM.render(<Mycomponent />, document.getElementById('test'))
注意

useRef数据发生变化时不会引发组件重新渲染

组件的capture value特性

函数组件的capture value特性

useRef与useState的区别以及使用场景

  • 区别

    • [1] useRef更新是同步的;useState更新是异步的。
    • [2] useState具有capture value特性;useRef可以跳过capture value特性
      • useState的值在每个render都是独立存在的
      • useRef更像是一个全局变量,每次都可以保持render的最新状态
    • [3]useState会触发组件重新渲染;useRef内容发生变化,不会引发组件重新渲染。
  • 以上区别就导致两者的使用场景不同

    • 若是常量直接定义在函数中(初始化和更新都会执行函数)
    • 若是需要试图更新的数据就使用useState
    • 其他需要保存的数据就使用useRef

props

函数组件也可以使用props进行数据传递

  • 在父组件中: 将需要传递给子组件的数据以属性的形式添加在子组件标签上。
  • 在自组件中:在自组件中以参数的形式进行接收。
    import {useState, createRef, useRef} from 'react'
    function Son(props){
      console.log('props', props) // props {name: 'chaochao'}
      return(
        <div>{props.name}</div>
      )
    }
    function App(){
      const [isHot, setFlag] = useState(false)
      return (
        <div>
          <Son name='chaochao'/>
        </div>
      )
    }
    

类型限制

类型限制与类组件的类型限制一样,在此不详细介绍~。

propTypes属性(prop-types库)
  • [1] 下载
    npm i prop-types
    
  • [2] 进行数据类型限制
    import PropTypes from 'prop-types'
    
      构造函数名.propTypes={
         属性名: propTypes.number // 要求数据格式为数字类型
      }
    
    举例说明
    import {useState, createRef, useRef} from 'react'
    import PropTypes from 'prop-types'
    
    function Son(props){
      console.log('props', props) // props {name: 'chaochao'}
      return(
        <div>{props.name}</div>
      )
    }
    Son.propTypes = {
      name: PropTypes.number
    }
    function App(){
      const [isHot, setFlag] = useState(false)
      return (
        <div>
          <Son name='chaochao'/>
        </div>
      )
    }
    
    此时在控制台会报如下警告
    学新通
    defaultProps属性
    defaultProps属性可以设置props属性的默认值,语法如下
      构造函数方法名.defaultProps = {
        sex: 'nv'
      }
    
    举例说明
    import {useState, createRef, useRef} from 'react'
    import PropTypes from 'prop-types'
    
    function Son(props){
      console.log('props', props) // props {name: 'chaochao'}
      return(
        <div>{props.name}<br />{props.sex}</div>
      )
    }
    Son.propTypes = {
      name: PropTypes.number
    }
    Son.defaultProps = {
      sex: 'nv'
    }
    function App(){
      const [isHot, setFlag] = useState(false)
      return (
        <div>
          <Son name='chaochao'/>
        </div>
      )
    }
    
    此时页面展示
    学新通

参考文档

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfiffef
系列文章
更多 icon
同类精品
更多 icon
继续加载