react函数式组件相关Hooks

函数式组件和类组件的区别?

1.语法上的区别: 类组件需要继承自Component,内部必须有render方法,必须return一个react元素, 类组件在语法上比函数式组件复杂.

2.类组件需要实例化然后才能通过this关键字操作组件本身. 函数式组件无需实例化. 函数式的渲染只需要调用函数即可, 执行效率较高.

3.类组件有自己的状态, 函数式组件没有自己的状态( 我们也讲这样的组件称为"无状态组件" ).

4.类组件有自己的生命周期, 函数式组件没有自己的生命周期.

5.类组件会实例化,效率低,有this,函数式组件没有this

Hooks配合函数式组件使用

//通过Hooks可以给函数式组件引入组件状态.

//通过Hooks可以给函数式组件引入生命周期.

//所谓的Hooks就是一系列的函数( api ), Hook叫做钩子函数, 所有钩子函数都是以use开头的

2. Hook 解决的问题

函数式组件不能使用 state:函数式组件比类组件更简洁好用,而 Hooks 让它更加丰

富强大;

副作用问题:诸如数据获取、订阅、定时执行任务、手动修改 ReactDOM 这些行为

都可以称为副作用;而 Hooks 的出现可以使用 useEffect 来处理这些副作用;

有状态的逻辑重用组件

复杂的状态管理:之前通常使用 redux、dva、mobx 这些第三方状态管理器来管理

复杂的状态,而 Hooks 可以使用 useReducer、useContext 配合实现复杂的状态管理;

开发效率和质量问题:函数式组件比类组件简洁,效率高,性能也好

3. useState

useState 用来管理组件的状态, 当下主要用来给函数式组件添加状态.

通过多次调用 useState(),一个函数组件可以拥有多个状态。

//useState 用来给函数式组件引入组件状态, 返回值是个数组, 数组的第一个元素是组件状态, 第二个元素是用来更新组件状态的函数, 调用该函数可以让当前组件更新

//useState 可以多次调用,给组件引入多个组件状态.

var [ count, setCount ] = useState(0);//setCount函数的参数是count变量的最新值

var [ str, setStr ] = useState('');//setStr函数的参数是str变量的最新值

4. useEffect

useEffect 用来给函数式组件添加生命周期方法。

函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class

的生命周期函数,你可以把 useEffect Hook 看做(类似于)

componentDidMount,

componentDidUpdate 和

componentWillUnmount 这三个函数的组合.

import './App.css';

import { useState, useEffect } from 'react';

function App() {

//useState 给组件添加状态

var [count, setcount] = useState(99)

// console.log(count);

//useEffect相当于给组件添加生命周期的

useEffect( //1.这种相当于componentDidMount和componentDidUpdate //挂载执行一次并且只要组件更新就会执行

() => {

console.log('组件更新了','wu');

},

)

useEffect( //2.这种相当于componentDidMount 只在初始挂载时执行

() => {

console.log('组件更新了','[]');

所以可以在这里发请求,创建定时器,事件监听

}, []

)

useEffect( //3.这种带return的这种相当于componentDidMount

() => {

console.log('组件挂载了');

// 可以在这里发请求,创建定时器,事件监听

var timerid = setInterval(() => {

console.log('定时器执行了');

}, 5000)

//相当于类组件的 componentWillUnmount

return () => {

//可以在这里取消请求,清除定时器,取消监听,进而避免内存泄漏

clearInterval(timerid)

}

}, []

)

useEffect( //4.这种可以监听[]中的数据变化,然后执行回调函数,count变化时执行

() => {

console.log('count变了',count); //这里可以拿到监听数据更新后的最新值

},[count]

)

var Change = () => {

setcount(count + 1)

console.log(count);//这里获取不到最新的count值,因为setcount是异步的

}

return (

{count}

);

}

export default App;

5. useRef

创建 ref,方便访问操作 DOM 节点,无法获取子组件实例(undefined)

import { useEffect, useRef, useState } from 'react';

import './App.css';

import Child from './components/Child'

function App() {

//使用useRef定义一个变量

var titleRef = useRef();

var childRef = useRef();

//只能在事件处理函数中,组件挂载完成的生命周期函数中获取dom节点

useEffect(()=>{

console.log( titleRef.current );

},[])

var handleClick = ()=>{

console.log( titleRef.current );//获取标签的dom节点

console.log( childRef.current );//获取子组件实例( 获取不到, 因为函数式组件不会实例化, 没有实例 )

}

return (

{/* 将useRef定义的变量绑定在标签的ref属性值的位置 */}

hello react

);

}

export default App;

6. Hooks 的使用规则

只在顶层调用 Hooks : Hooks 的调用尽量只在顶层作用域

不要在循环、条件或嵌套函数中调用 Hook,否则可能会无法确保每次组件渲染时都

以相同的顺序调用 Hook

只在函数组件调用 Hooks:目前只支持函数式组件,未来版本 Hooks 会扩展到 class

类组件;

React Hooks 的应用场景:函数式组件、自定义 Hooks

1. hooks 的其他应用

useContext (跨组件传值)

用来解决同一个父组件的后代组件之间的数据共享问题, (跨组件通信) 同一个父组件的所有后代

组件都可以用 useContext() 从最近的 context 中获取.(函数式组件没有this所以要用这个钩子)

通信对象:

react函数式组件相关Hooks_第1张图片

外层组件:

react函数式组件相关Hooks_第2张图片

函数式组件内层组件想接收:

react函数式组件相关Hooks_第3张图片

类组件里内层是这样接收的

react函数式组件相关Hooks_第4张图片
react函数式组件相关Hooks_第5张图片

useMemo

性能优化,利用了闭包的特性,会返回一个记忆值, 通过记忆值来避免在每个渲染上都执行高开销的计算(计算缓存);适用于复杂的计算场景,

如复杂的列表渲染,对象深拷贝...

useMemo() 接收 2 个参数:一个计算函数 和 依赖数组。

计算函数: 返回一个函数

依赖数组: 放置依赖的数据

//useMemo的返回值是一个记忆值( 计算得到的值 ), useMemo会监听[]中值的变化

//组件更新时, 执行到useMemo函数的位置, 如果监听的值没有发生改变,

则会跳过回调函数的执行,直接返回上次计算的记忆值.

//组件更新时, 执行到useMemo函数的位置, 如果监听的值改变了,

则重新执行回调函数,计算得到一个新的记忆值并返回.

import { useEffect, useMemo, useRef, useState } from 'react';

import './App.css';

function App() {

var [num,setNum] = useState(0)

var [count,setCount] = useState(0)

var handleChange = (e)=>{

setNum(e.target.value);

}

var handleClick = ()=>{

setCount(count+1);

}

// var calc = ()=>{

// console.log('calc');

// var sum = 0;

// //计算从1到num 每个整数的和

// for(var i = 1;i <= num;i++){

// sum += i;

// }

// return sum;

// }

//useMemo的返回值是一个记忆值( 计算得到的值 ), useMemo会监听[]中值的变化

//组件更新时, 执行到useMemo函数的位置, 如果监听的值没有发生改变,则会跳过回调函数的执行,直接返回上次计算的记忆值.

//组件更新时, 执行到useMemo函数的位置, 如果监听的值发生改变,则重新执行回调函数,计算得到一个新的记忆值并返回.

var calc = useMemo(()=>{

console.log('calc');

var sum = 0;

//计算从1到num 每个整数的和

for(var i = 1;i <= num;i++){

sum += i;

}

return sum;

},[num])

return (

1-num的和: {calc}

);

}

export default App;

useCallback

useCallback(callback, array):也是用于性能优化,与 useMemo()不同的是,返回值是

callback 本身;

//useCallback 的返回值是一个记忆的函数( 就是他的第1个参数 ), useCallback会监听[]中值的变化

//组件更新时, 执行到useCallback函数的位置, 如果监听的值没有发生改变,

则会忽略第一个参数,直接返回上次生成的回调函数.

//组件更新时, 执行到useCallback函数的位置, 如果监听的值改变,

则重新生成一个新的回调函数,并返回该函数.

//注意: useCallback 需要配合子组件的memo高阶组件一起使用, 才能起到控制子组件更新的作用( 避免子组件的无效更新, 实现性能优化 ).

父组件:使用useCallback 配合子组件memo

父组件更新时传入子组件props变化时子组件再更新不然不更新

react函数式组件相关Hooks_第6张图片

子组件 用memo配合父组件

react函数式组件相关Hooks_第7张图片
react函数式组件相关Hooks_第8张图片

2.react-router 相关的 Hooks

路由相关的hooks:

import {useNavigate, useLocation, useParams, useSearchParams } from "react-router-dom"

useRoutes (配置路由规则)

配合函数式组件配置路由(负责动态生成整个应用的路由规则)

react函数式组件相关Hooks_第9张图片

useNavigate (路由跳转)

调用useNavigate得到一个用来跳路由的api(编程式导航:通过执行js代码跳转路由)

react函数式组件相关Hooks_第10张图片

useLocation (获取state形式路由参数)

react函数式组件相关Hooks_第11张图片

useParams (获取动态路由参数)

react函数式组件相关Hooks_第12张图片

useSearchParams (获取字符串形式的路由参数)

react函数式组件相关Hooks_第13张图片

v6路由

(之前类组件配合使用的是v5路由)

1.安装

npm i react-router-dom –save

路由管理组件

react-router-dom包含多种路由管理组件: BrowserRouter , HashRouter , MemoryRouter , NativeRouter , StaticRouter ;

打开入口文件,导入路由管理组件,包裹根组件

react函数式组件相关Hooks_第14张图片

路由规则方式1:

(先导入相关组件)

v6中 Switch 换成了 Routes

v6中 Redirect 组件没有了,重定向用 Navigate组件

v6中 路由规则无需添加exact精确匹配

v6中 嵌套路由规则是直接写在指定父路由规则内部

图示包含嵌套路由(二级路由)

二级路由对应得组件渲染位置需要写占位(路由对应得组件渲染出口)

react函数式组件相关Hooks_第15张图片

路由规则方式2:

①先在router文件里配置路由表

//配置路由表

export default [

{

path:'/index',

element: ,

children:[ //嵌套路由

{

path:'/index/home',

element: ,

},

{

path:'/index/cate',

element: ,

},

{

path:'/index/mine',

element: ,

},

{

path:'/index',

element: ,

},

{

path:'*',

element: ,

},

]

},

{

path:'/login',

element:

},

{

path:'/register',

element:

},

{

// path:'/detail/:id',

path:'/detail',

element:

},

{

path:'/',

element:

},

{

path:'*',

element:

},

]

②然后再导出路由表,在路由规则处用useRoutes如下:

import routes from '../router/routes'

{

useRoutes(routes)

}

或者

把这段代码写成一个组件如下:

react函数式组件相关Hooks_第16张图片

在再路由规则处写:

路由跳转

a) 声明式导航

react函数式组件相关Hooks_第17张图片
react函数式组件相关Hooks_第18张图片

组件渲染位置要用

{/* OutLet是个占位组件( 动态组件 ), 类似于vue-router中的router-view */}

a) 编程式导航

useNavigate

import { useNavigate} from 'react-router-dom'

var navigate = useNavigate()

// navigate('/index/cate');

// navigate({ pathname:'/index/cate' });

// navigate(-1);

react函数式组件相关Hooks_第19张图片

1. 路由参数

a) 动态路由传参

动态路由: 路由地址中带有 :xx 这样的路由地址就是动态路由. ( 刷新页面, 参数不消失,参数会在地址栏显示)

定义动态路由

}>

跳路由

详情

或者

navigate({ pathname:'/detail/'+88888 });

navigate( "/detail/8888" )

获取路由参数

组件内, 通过 useParams() 接收传递过来的参数

var params=useParams()

console.log(params);

var { id } = useParams()

console.log(id);

a) 通过search查询字符串传参

定义路由

}>

跳路由

详情

或者

navigate( "/detail?id=8888" )

navigate( {pathname:'/detail' , search : 'id=8888&name=abc'} );

获取路由参数

组件内, 通过 useSearchParams( ) 接收传递过来的参数

var [ params, setParams ] = useSearchParams( )

console.log(params.getall())

console.log(params.get('id'));

console.log( params.get('name') );

a) 通过state传参

定义路由

}>

跳路由

XX

跳路由

获取路由参数

组件内, 通过 useLocation() 接收传递过来的参数

var location = useLocation( )

console.log(location.state);

state传参, 刷新页面后参数不消失,state传的参数是加密的

示例:

react函数式组件相关Hooks_第20张图片
react函数式组件相关Hooks_第21张图片

你可能感兴趣的:(react)