React——函数组件

本文详细介绍了React函数组件的创建、事件处理和Props的使用,探讨了有状态和无状态组件的区别。进一步,文章深入讲解了React Hooks,包括useState、useEffect、useRef、useMemo以及React.memo和useCallback的基本概念和用法,展示了如何在函数组件中管理状态和执行副作用操作。此外,还提到了自定义Hook的创建及其在提取组件公共逻辑中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、函数组件的创建

1、函数组件的创建

函数组件:使用JS的函数(或箭头函数)创建的组件称为函数组件,函数组件的规定

函数名称必须以大写字母开头

函数组件必须有返回值,返回JSX表达式

渲染函数组件:函数名作为组件标签名

组件名称可以是单标签也可以是双标签

2、函数组件事件处理

注意:函数组件中绑定方法不需要this

3、函数组件的Prop

3.1、函数组件的Props

props就是传递的JS对象的值

3.2、props默认值

首先,先来完成一个函数组件的默认值案例

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

function UserInfo(props){
    const template=(
        <div>
            <ul>
                <li>姓名:{props.username}</li>
                <li>年龄:{props.age}</li>
                <li>特长:{props.specialty}</li>
            </ul>
        </div>
    );
    return template;
}
//设置默认值的语法 组件名.defaultProps
UserInfo.defaultProps={
    username:'无名氏',
    age:0,
    specialty:'无特长'
}
const template=(
    <div>
        <UserInfo username='张明理'></UserInfo>
    </div>
);
ReactDOM.render(template,document.getElementById('root'));

这里如果在调用组件的时候赋值,那么默认值就不会生效了,倘若,默认值就会生效。

3.3、props验证器

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

function UserInfo(props){
    const template=(
        <div>
            <ul>
                <li>姓名:{props.username}</li>
                <li>年龄:{props.age}</li>
                <li>特长:{props.specialty}</li>
            </ul>
        </div>
    );
    return template;
}
UserInfo.propTypes={
    username:PropTypes.string.isRequired,
    age:PropTypes.number.isRequired,
    specialty:PropTypes.string
}
const template=(
    <div>
        <UserInfo age={'20岁'} specialty='打羽毛球'></UserInfo>
    </div>
);
ReactDOM.render(template,document.getElementById('root'));

4、有状态组件和无状态组件

  • 函数组件又叫做无状态组件,类组件又叫做有状态组件
  • 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
  • 函数组件没有自己的状态,只负责数据展示
  • 类组件有自己的状态,负责更新UI

二、Hooks简介

1、hook的由来和作用

react16.8以后的新特性Hooks函数组件在react16.8以前函数组件只能被动接收外部数据,并且没有自己的生命周期钩子函数,函数内部也没有this可用新特性Hookhook推出的动机主要是因为类组件有一下几个不足

  • 组件之间复用公共逻辑比较麻烦,以前常见的提取组件公共逻辑的方式有高阶组件/renderProps等,但这些方式或多或少都对原有组件的代码组织方式有一定的破坏性
  • 复杂组件变得难以理解(例如相同的逻辑可能要写在不同的生命周期钩子函数里面)
  • 难以理解的class(比如对新手来见,class组件中的this不太好理解)

新特性hook出现之后,函数组件就可以完全替代类组件,但是这并不是说react官方会抛弃类组件,react官方文档中也表明了没有计划会在react中移除class组件。

注意:hook特性只能在函数组件中使用

三、State Hook

1、作用

useState 为函数组件提供了状态(state)

2、基本使用

2.1、使用步骤

  • 导入useState函数
import { useState } from 'react'
  • 调用useState函数,并传入状态的初始值,从useState函数的返回值中,拿到状态和修改状态的方法
const [变量名, 方法名] = useState(数据初始值);
  • 在jsx中展示状态
  • 使用修改状态的方法更新状态

2.2、代码实现

import { useState } from 'react'

export default function Counter() {
    const [count, setCount] = useState(0);
    const decrement = () => {
        setCount(count - 1)
    }
    return (
       <>
         <h1>计数器:{count}</h1>
         <button onClick={decrement}>-1</button>
         <button onClick={() => {setCount(count + 1)}}>+1</button>
        </>
    )
}

注意:修改数据的 set 方法,只需要接收一个新数据作为参数,方法内部会自动用接收到的新数据来覆盖旧数据。

3、函数作为参数

  • 语法
const [变量名, 方法名]=useState(()=>{return '计算之后的初始值'})
  • 语法规则
    • 回调函数return出去的值作为变量名的初始值
    • 回调函数中的逻辑只会在组件初始化的时候执行一次
  • 语法选择
    • 如果初始化一个普通的数据,直接使用useState(初始值)即可
    • 如果要初始化的数据无法直接得到需要通过计算才能获取到,使用useState(()=>{})
  • 案例实现
import {useState} from 'react'
function Counter(props){
  const [count,setCount]=useState(()=>{
    return props.count
  })
  return(
    <>
      <h2>{count}</h2>
      <button onClick={()=>setCount(count+1)}>+</button>
    </>
  )
}
function App() {
  return(
    <>
      <Counter count={10}></Counter>
      <Counter count={20}></Counter>
    </>
  )
}
export default App;

四、 Effect Hook

1、作用

副作用:副作用是相对与主作用而言的,一个函数除了主作用,其他都是副作用,对于React组件来说,主作用是根据数据(state/props)渲染UI,除此之外都是副作用

常见的副作用

        数据请求Ajax发送

        手动修改DOM

        loaclStorage操作

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

import {useState,useEffect} from 'react'

export default function Example() {
  const [count,setCount]=useState(0)
  useEffect(()=>{
      document.title=`You clicked ${count} times`
  })
  return (
    <div>
        <p>You clicked {count} times</p>
        <button onClick={()=>{setCount(count+1)}}>Click Me</button>
    </div>
  )
}

2、useEffect 的参数

import { useEffect } from 'react'
useEffect(() => {}, []);

useEffect 的参数,主要分为两个,第一个固定是一个回调函数,第二个是一个数组(可选)。

参数传递的不同,useEffect 实现的效果也不同。

2.1、没有第2个参数

当 useEffect 只有第一个参数时,其作用就是在模拟 componentDidMount 和 componentDidUpdate 生命周期函数。

也就是说,useEffect 的第一个回调函数,会在组件首次挂载完成执行一次,同时,后续组件每次更新完成时也会执行。

useEffect(() => {
    // ...
})

执行的效果是:组件首次挂载完成执行一次,每次单击按钮会执行一次

2.2、第2个参数是空数组

当 useEffect 的第二个参数是一个空数组是,其作用就是在模拟 componentDidMount 生命周期函数。

也就是说,useEffect 的第一个回调函数,会在组件首次挂载完成执行一次。

useEffect(() => {
    // ...
}, [])

关键代码如下所示

import {useState,useEffect} from 'react'

export default function Params() {
  const [count,setCount]=useState(0)
  useEffect(()=>{
    console.log('没有第2个参数');
  },[])
  return (
    <div>
        <h1>{count}</h1>
        <button onClick={()=>setCount(count+1)}>+1</button>
    </div>
  )
}

执行的效果是:只有在组件首次挂载完成执行一次,后续单击不会执行

2.3、第2个参数是非空数组

当 useEffect 的第二个参数是一个非空数组时,其作用就是在模拟 componentDidMount 生命周期函数,同时还可以模拟 Vue 中 watch 的作用。

也就是说,useEffect 的第一个回调函数,会在组件首次挂载完成执行一次。后续,只要当第二个参数的数组中,任意一条数据发生改变,useEffect 的第一个回调函数又会再次执行。

useEffect(() => {
    // ...
}, [数据1, 数据2, ...])

关键代码如下所示

import {useState,useEffect} from 'react'

export default function Params() {
  const [count,setCount]=useState(0)
  const [name,setName]=useState('张三')
  useEffect(()=>{
    console.log('第2个参数是非空数字');
  },[count])
  return (
    <div>
        <h1>{count}</h1>
        <h1>{name}</h1>
        <button onClick={()=>setCount(count+1)}>+1</button>
        <button onClick={()=>setName('李四')}>更新</button>
    </div>
  )
}

执行的结果是:在组件首次挂载完成执行一次,后续只有在点击+1操作按钮的时候才会调用useEffect的回调函数。单击更新按钮不会触发useEffect回调函数的执行

3、清理副作用

当 useEffect 的第一个参数中,返回了一个函数。那么,返回的这个函数就是在模拟 componentWillUnmount 生命周期函数。

在组件被销毁时,如果有些副作用操作需要被清理,在这里可以写清理副作用的代码。

useEffect(() => {
    return () => {

    }
})

关键代码

import { useEffect } from 'react'

export default function ClearEffect() {
    useEffect(() => {
        let times=setInterval(() => {
            console.log('定时执行操作');
        }, 1000);
        return ()=>{
            clearInterval(times)
        }
    }, [])
    return (
        <div>this is test</div>
    )
}
***************************************************************************************
import ClearEffect from "./ClearEffect"
import { useState } from "react"
export default function App() {
  const [flag,setFlag]=useState(true)
  return (
    <div>
      {flag? <ClearEffect/>:null}
      <button onClick={()=>setFlag(!flag)}>switch</button>
    </div>
  )
}

五、 useRef

1、作用

在函数组件中获取DOM元素或者组件对象

2、使用步骤

  • 导入useRef函数
import { useRef } from 'react'
  • 执行useRef函数,返回一个ref对象
function 组件名() {
    const 变量名 = useRef();
}
  • 绑定 ref

找到函数组件中对应的节点,通过 ref 属性将 ref 对象绑定到元素节点身上:

function 组件名() {
    const 变量名 = useRef();
    return (
        <节点 ref={变量名}></节点>
    )
}
  • 获取元素节点

当元素通过 ref 绑定了元素后,就可以通过 ref 对象来获取元素节点了:

function 组件名() {
    const 变量名 = useRef();
    return (
        <节点 ref={变量名} onClick={() => {
            console.log(变量名.current);   // 元素节点    
        }}></节点>
    )
}

六、useMemo

1、作用

useMemo的作用类似vue中的计算属性computed,即根据母体数据衍生出新的数据,一旦母体数据发生变化,则useMemo会自动执行

2、基础语法

import { useMemo } from 'react'

const 变量名 = useMemo(() => {
    return 计算得到的数据;
}, [依赖的原数据])

最终,计算得到的数据,就会通过 return 保存到变量身上。

关键代码如下所示

import {useState,useMemo} from 'react'

export default function App() {
  const [fnum,setFnum]=useState(10)
  const [snum,setSnum]=useState(20)
  const sum=useMemo(()=>{
    return fnum+snum
  },[fnum,snum])
  return (
    <div>
      <h1>{sum}</h1>
      <button onClick={()=>setFnum(fnum+2)}>fnum+2</button>
      <button onClick={()=>setSnum(snum+3)}>snum+3</button>
    </div>
  )
}

七、React.memo和useCallback

1、React.memo

React.memo 可以用来缓存组件。

在 React 中,默认情况下,父组件更新时,无论子组件内部是否改变,子组件也会更新。

但是,从性能考虑,我们希望的是:父组件更新时,如果子组件内部(state 和 props)没有发生改变,子组件不用更新。

解决方法,就是用 React.memo 将子组件缓存起来。

import {useState} from 'react'
import HookChild from './HookChild'

export default function HookParent() {
  console.log('父组件');
  const [count,setCount]=useState(0)
  return (
    <div>
        <h1>父组件:{count}</h1>
        <button onClick={()=>setCount(count+1)}>+1</button>
        <HookChild></HookChild>
    </div>
  )
}

import React from 'react'

function HookChild() {
    console.log('子组件');
    return (
        <h2>子组件</h2>
    )
}

export default React.memo(HookChild);

2、useCallback

useCallback 可以用来缓存函数。

2.1、基础语法

import { useCallback } from 'react';

const 变量名 = useCallback(缓存的函数, [])

2.2、应用场景

通常,我们为了减少子组件不必要的更新,会使用 React.memo() 来缓存子组件的状态。

但是,如果父组件传递了函数给子组件,那么,每次父组件更新时,函数都会重新创建。子组件中的 React.memo() 就会判定为父组件传递的内容发生了改变,那么 React.memo() 就不会缓存当前子组件的状态。

因此,为了解决函数的问题,我们需要保存函数的状态,让父组件更新时函数能够缓存下来,因此我们需要使用 useCallback 将传递的子组件的函数状态缓存起来。

import { useCallback, useState } from "react";
import HookChild from "./HookChild";

export default function HookFather() {
    console.log('父组件');
    const [count, setCount] = useState(0);

    const sayHello = () => {
        console.log('hello');
    }
    // 缓存函数状态
    const cbSayHello = useCallback(sayHello, []);

    return (
        <>
            <h1>父组件:{count}</h1>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <HookChild sayHello={cbSayHello}></HookChild>
        </>
    )
}
import React from 'react'
function HookChild({sayHello}) {
  console.log('子组件');
  return (
    <div>
        <h2>子组件</h2>
    </div>
  )
}
export default React.memo(HookChild)

八、自定义hook

自定义hook提取函数组件公共逻辑

  • 自定义hook的作用在于提取公共逻辑,所以一般不会返回一个JSX对象,而是会根据需要返回特定的数据或者方法
  • 自定义hook必须要以use开头

假设现在有Test1和Test2两个函数组件,他们有一些公共逻辑需要提取,比如都要从后台获取一个列表数据,然后用户界面的渲染,则现在可以编写一个自定义hook来提取这部分公共逻辑,以避免重复写代码

import {useState,useEffect} from 'react'
export default function useData(){
    const [list,setList] = useState([]);
 useEffect(()=>{setTimeout(()=>{            
        const list = [1,2,3]            
        setList(list);        
    },200);},[]);
    return {list,}}
//Test1
import useData from '...'
function Test1(){
    const {list} = useData();
    return 
    div>
        {...用list渲染JSX}
    </div>
}
//Test2类似
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值