React基础语法入门,React框架hooks函数基础用法

205 阅读8分钟

前言

时间过得太快 一晃就入行前端开发三年了,从入行至今做过各种框架的项目,其中主流的react很久没用了,最近刚好复习了一下顺便做成文章分享一下也方便自己日后回忆,有开发经验的前端看完这个系列的文章已经足够参与普通管理系统项目的编写了(包含了class写法的hooks写法), 本系列共有五个章节:

  1. -JSX语法
  2. -组件基础
  3. -组件通讯与组件进阶
  4. -day5_hooks基础
  5. -路由用法

适用人群: 没时间去看官方学习或者找视频进行系统学习,但又临时接手react项目需要快速上手的的小伙伴;或者会vue框架但想快速多学一门框架方便日后上手的小伙伴

正文

前面三章把JSX语法和组件通讯都讲解了,接下来是当前项目使用比较多语法函数式组件的hooks,这是当前市面上项目的绝对主流写法,这部分我会用特别多注释去说明解释,得好好消化了。

1、useState基础使用

import {useState} from "react";  

function App(){
 // const [fkat,setFlat] = useState(true)   //可以定义多个  ,会按顺序执行
 //格式: const [变量,改变变量的方法] = useState(默认值)
  const [count,setEvent] = useState(0)  
  return(
    <div>
      {/* 实现一直点击按钮就一直自增 */}
      <button  onClick={()=>setEvent(count+1)}>{count}</button>
    </div>
  )
}

export default App;

步骤:
  1.导入useState函数
  2.执行函数并且传入初值,必须在函数组件中
  3.[数据,修改数据的方法]
  4.使用数据
具体说明:在第6行定义了count变量,在第10行改变变量count, useState用法格式:const [变量, 改变变量的方法] = useState(默认值) ,变量和改变变量的方法是成对使用的
使用规则注意:
  1.只能出现在函数组件
  2.不能嵌套在if/for/其它函数中(react按照调用顺序识别每一个hook)
  3.可以通过开发者工具查看hooks状态
注意:
  1.[数据,修改数据的方法] 顺序不能更换,第一个参数是变量第二个是对方的修改方法是固定的
  2.conunt和setEvent是绑在一起的,只能用来修改对应的count值

2、useEffect理解副作用和基础使用

import {useState,useEffect} from "react";  

function getNum(a,b){
  return a+b
}

function App(){
 
  const [count,setEvent] = useState(0)  
  const [name,setName] = useState('tt')  
   //用法1.默认状态(无依赖项)----初始化时执行一次,等到每次数据修改组件更新再执行
  useEffect(()=>{    
    console.log('执行了');
  })  

 //用法2.添加空数组 ------ 初始化时执行一次,后面更新不再执行了
   useEffect(()=>{    
      document.title = count
   },[])  

//用法3.添加特定的依赖 ---- 初始化时执行一次,等到每次数据修改组件更新再执行(只会添加依赖的count触发,name不会触发)
    useEffect(()=>{    
      console.log('执行了');
    },[count])     
    
    return(
      <div>
        <button  onClick={()=>setEvent(count+1)}>{count}</button>
        <button  onClick={()=>setName('cp')}>{name}</button>
      </div>
    )
}

export default App;

在函数中执行,传入回调 并且定义副作用 当我们通过修改状态更新组件时,副作用也会不断执行 ,useEffect通过依赖项控制副作用的执行时机----类似于vue的监听和mounted生命周期 具体用法有三种:
  1.默认状态(无依赖项)----初始化时执行一次,等到每次数据修改组件更新再执行
  2.添加空数组 ------ 初始化时执行一次,后面更新不再执行了(类似vue的mounted)
  3.添加特定的依赖 ---- 初始化时执行一次,等到每次数据修改组件更新再执行(只会添加依赖那个触发类似vue的watch监听)

3、useState函数作为参数

使用场景:参数只在组件初始渲染中起作用,后续渲染时忽略。如果初始state需要通过计算获得, 可以传入一个函数,在函数中计算并返回state,此函数只在初次渲染时被调用

语法: const [namae,getName] = useState(()=>{编写计算逻辑 return ‘计算之后的初始值’ })

import React, { useState } from "react";

//实现一个自增按钮,可以由使用的时候以传参的方式决定递增的初始值是什么
function Counter(props){
  const [name,setName] = useState(()=>{    //这里只是为了示范,其实中途可以进行很多处理的
    return props.name
  })
  return (
    <div >
     <button onClick={()=>setName(name+1)}>{name}</button>
 </div>
  )
}

function App(){
  return(
    <>
    <Counter name={15}></Counter>
    <Counter name={10}></Counter>
    </>
  )
}

export default App;

上述第10行,直接传入一个函数return作为默认值

4、useEffect清除副作用

任务:掌握清理useEffent的方法

使用场景:在组件被销毁时,如果有副作用的操作需要被清理,可以使用此方法,比如常见的定时器

注意:其实就是类似于类组件和vue在销毁生命周期清除定时器,但函数组件没有生命周期所以这样处理

实现:添加副作用函数前,组件虽然已经不显示了,但是定时器依旧在运行,所以要清除动作

import React, { useState ,useEffect} from "react";

function Counter(){
  useEffect(()=>{
  //如果没有下面的清理动作,会在组件不显示时,定时依然执行,因为没有清除定时器
   let timer = setInterval(()=>{               
      console.log('定时器在执行');
    },1000)
    return () =>{    //清理动作---------------------
      clearInterval(timer)
    }
  },[])
  return (
    <div >
    我是显示的组件
 </div>
  )
}

function App(){
  const [flat,setFlat] = useState(true)
  return(
    <>
    <button onClick={()=>setFlat(!flat)}>点击</button>
   {flat ?<Counter ></Counter>:null } 
    </>
  )
}

export default App;

在第15行使用return ()=>{ 执行逻辑 } 这个语法结构去清除定时器, 其实就是类似于类组件和vue在销毁生命周期清除定时器,但函数组件没有生命周期所以这样处理

5、useEffent发送网络请求

任务:掌握useEffent发送网络请求-类组件则在componentDidMount中请求接口(类似VUE生命周期请求)

使用场景:如何在useEffect中发送网络请求,并且封装同步async await操作

注意:不可能直接在useEffect回调函数外面包裹await,因为异步会导致清理函数无法立即执行

import React, { useState ,useEffect} from "react";


function Counter(){
  //因为“,[]”写法只在初始化时执行一次,所以可以用来请求接口--
  useEffect(()=>{
    async function fetchData(){
       const res  = await axios.get('http://geek.itheima.net/v1_0/channels') 
    }
    fetchData()
  },[])     
  
  return (
  )
}

function App(){
  return(
    <>
       <Counter ></Counter>
    </>
  )
}

export default App;

前面我们讲解useEffent时说到 useEffent同时像vue的watch和mounted,第10到第15行这里就是像mounted使用场景

6、useRef使用

import React, { useEffect, useRef} from "react";   //1.导入useRef

class TestC extends React.Component{
  render(){
    return(
      <div>我是类组件</div>
    )
  }
}

function App(){
   //2.执行useRef函数并传入null,返回值为一个对象,内部有一个current属性存放拿到的DOM对象(组件实例)
  const testRef = useRef(null)  
  const divRef = useRef(null) 

useEffect(()=>{//注:useEffect是在dom渲染后才执行,所以能获取到(vue的watch是一开始就执行)
  console.log(testRef.current);  //TestC {props: {…}, context: {…}, refs: {…} …}
  
  console.log(divRef.current);  // div DOM 节点
},[])

  return(
    <>
    {/* 3.通过ref绑定 要获取的元素或者组件 */}
    <TestC ref={testRef}></TestC>    
    <div ref={divRef}>我是函数组件</div>
    </>
  )
}

export default App;

按上面行1、12、22、的标注顺序进行操作; 具体思路就是
  1.导入useRef
  2.执行useRef函数并传入null,返回值为一个对象,内部有一个current属性存放拿到的DOM对象(组件实例)
  3.通过ref绑定 要获取的元素或者组件

注:

(1)子组件是类组件可以使用useRef直接获取到DOM,函数组件的子组件需要用forwardRef包起来,如下代码示例第4、6行是重点

(2)useEffect是在dom渲染后才执行,所以能获取到(vue的watch是一开始就执行)

// TestC 函数组件,使用 forwardRef
import React, { useEffect, useRef, forwardRef, useImperativeHandle } from "react";

const TestC = forwardRef((props, ref) => {
  // 暴露方法给父组件
  useImperativeHandle(ref, () => ({
    sayHello: () => {
      alert("Hello from TestC");
    }
  }));

  return <div>我是函数组件TestC</div>;
});

function App() {
  const testRef = useRef(null);
  useEffect(() => {
    console.log(testRef.current); // { sayHello: ... }
    // 可以调用 testRef.current.sayHello();
  }, []);

  return (
    <>
      <TestC ref={testRef} />
    </>
  );
}

export default App;

思路说明

(1)在函数组件中,ref 默认指向 DOM 节点。如果你希望 ref 指向自定义方法或属性,必须用 useImperativeHandle

(2)如果不使用 useImperativeHandle,ref 只能拿到 DOM 节点,无法拿到自定义方法。

(3)只有类组件可以直接通过 ref 拿到实例和方法,函数组件必须用 useImperativeHandle

7、useCallback

useCallback 专门用于缓存函数,避免在每次渲染时创建新的函数实例,从而提高性能。

基本语法

依赖a,b发生变化时才触发方法

import React, { useState, useCallback } from 'react';

const CallbackExample = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  // 1、普通函数 - 每次渲染都会创建新实例
  const handleClick1 = () => setCount(c => c + 1);

  // 2、使用 useCallback --- 后面[] 无依赖,函数永不重新创建
  const handleClick2 = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  // 3、有依赖的 useCallback --- 当namee 改变时,函数才会重新创建
  const handleNameChange = useCallback((newName: string) => {
    setName(newName);
  }, [name]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick1}>普通函数</button>
      <button onClick={handleClick2}>useCallback 函数</button>
      <input value={name} onChange={e => handleNameChange(e.target.value)} />
    </div>
  );
};

8、useMemo

用于缓存计算结果,避免在每次渲染时重复执行昂贵的计算。

useMemo 返回一个记忆化的值。这个值可以是任何类型,包括对象、数组、或者像 JSX 元素。

import React, { useState, useMemo } from 'react';

const MemoExample = () => {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState(['task1', 'task2']);

  // 昂贵计算 - 每次渲染都执行
  const expensiveValue1 = (() => {
    console.log('执行计算...');
    return count * 2;
  })();

  // 使用 useMemo - 只有 count 变化时才重新计算
  const expensiveValue2 = useMemo(() => {
    console.log('执行计算...');
    return count * 2;
  }, [count]); // 只有 count 变化时重新计算

  // 过滤操作优化
  const completedTodos = useMemo(() => {
    return todos.filter(todo => todo.includes('complete'));
  }, [todos]); // 只有 todos 变化时重新过滤

  return (
    <div>
      <p>Count: {count}</p>
      <p>计算结果1: {expensiveValue1}</p>
      <p>计算结果2: {expensiveValue2}</p>
      <p>完成任务数: {completedTodos.length}</p>
      <button onClick={() => setCount(c => c + 1)}>增加计数</button>
      <button onClick={() => setTodos([...todos, 'new task'])}>添加任务</button>
    </div>
  );
};

小结:

感觉这几部分够基本写页面了,日常项目基本就是useState定义数据、处理数据; useEffect监听数据变化、发送接口获取数据, 后面最后一章我再讲一下路由跳转基本就完结入门了