小埋摸鱼学习法 - react
type: channel
link: https://www.topzhang.cn/category/react/
items: React的 Suspense 以及使用场景
items: 虚拟滚动
items: koa+node+redis实现注册图片验证码功能
items: React-router实现路由拦截
items: React使用动画
items: React中书写css代码
items: Redux的使用
items: React的setState详解
items: React Hooks 使用介绍
items: React-router V6 使用

React的 Suspense 以及使用场景
type: item
date: 2025-07-05T08:35:00+00:00
description: React的 SuspenseReact的内置组件 中文意为 悬念 ，所以表现的是一种拿不准的状态，该组件的作用是，作为一个包裹组件，包裹我们需要加载的子级组件，在子级组件未加载完毕的时候展示替代组件（比如loading组件）<Suspense fallback={<Loading />}> <SomeComponent /> </Suspense>本文直接对目前Suspense的使用方法做一个直白讲解，Suspense的演变过程以及理念可以看文末的参考链接详细了解。常用用法Suspense 结合Data Fetch在我刚开始学习React的时候，Suspense还只能React.lazy结合使用用于加载异步组件。而现在Suspense已经可以对任何异步状态生效了,当然这需要遵守一定的规则。Suspense结合 Data Fetch的代码如下：import { fetchData } from './data'; export default function Albums() { const albums = use(fetchData(`/the-beatles/album`)); console.log('albums', albums); return ( {albums.map((album) => ( {album.title} ({album.year}) ))} ); } // This is a workaround for a bug to get the demo running. // TODO: replace with real implementation when the bug is fixed. function use(promise) { console.log('promise', promise); if (promise.status === 'fulfilled') { return promise.value; } else if (promise.status === 'rejected') { throw promise.reason; } else if (promise.status === 'pending') { throw promise; } else { promise.status = 'pending'; promise.then( (result) => { promise.status = 'fulfilled'; promise.value = result; }, (reason) => { promise.status = 'rejected'; promise.reason = reason; }, ); console.log('throw', promise); throw promise; } } 注意上述代码中的use方法就是将我们的异步请求方法转换为能够符合suspense用法的函数，即要求被Suspense包裹的组件是能够抛出一个Promise 异常。在这个 Promise 结束后再渲染组件，因此use函数需要在 Pending 状态时抛出一个 Promise，使其可以被 Suspense 捕获到。Suspense和lazy结合使用这个就是最开始大家都了解的使用方法了import React, { Suspense, lazy } from 'react'; const AsynComponent = lazy( async () => await lazyLoad(import('./lazyLoad')), ); export default function Index() { return ( <Suspense fallback={ loading.... }> <AsynComponent /> </Suspense> ); } // 模拟加载组件的延迟 async function lazyLoad(promise) { await new Promise((resolve) => { setTimeout(resolve, 2000); }); return promise; }动态加载外部组件最近工作中有做到在pc端动态加载H5组件资产的需求，用于展示发布的组件。这里我利用suspense和错误边界来进行一个封装，让其成为一个通用的组件。代码示例：错误边界组件这里按照需求自定义即可// ErrorBoundary.tsx import React, { ErrorInfo, ReactNode } from 'react'; interface Props { fallback: ReactNode; childr…

虚拟滚动
type: item
date: 2023-07-16T09:06:00+00:00
description: 虚拟滚动虚拟滚动在前端是一个很常见的性能优化解决方案。当我们需要渲染大量的dom的时候（比如几十万个列表数据），由于浏览器的内存限制，几十万个dom节点全部渲染出来就会导致页面滚动卡顿。而虚拟滚动就是模拟浏览器的原生滚动方式，按需加载列表元素，避免大量渲染dom元素造成卡顿。基本思路根据容器高度和每个列表项的高度计算出我们需要展示多少条数据发生滚动的时候记录滚动高度根据滚动的高度截取应该展示数组中的那些数据对于每个列表项的高度，我们这里可以分为定高的列表项和不定高的列表项定高定高的思路比较简单，首先初始化的时候，记录容器的高度，列表项的高度，从而计算出，当前视口要展示的元素的起始索引，/** * list 带有高度的数组 * offset 滚动offset */ const findIndexOverHeight = (list: ICalculationList[], offset: number) => { let currentHeight = 0; for (let i = 0; i < list.length; i++) { currentHeight += list[i].domHeight; if (currentHeight > offset) { return i; } } return list.length - 1; } 我们的开始索引就是offset=0 所计算出来的位置，结束索引就是 offset+containerHeight 所计算出来的高度let offset = 0; let start = findIndexOverHeight(list, offset); let end = findIndexOverHeight(list, offset+containerHeight);所以我们根据上述的函数就可以计算出来视图展示所要的数据范围了，再使用slice(startIndex,endIndex)就可以截取需要展示的数据，然后我们再将这些数据渲染到视图中。但是上述渲染出来后可以发现，我们都是从头到位完整的渲染这些数据项，而滚动有时候并不会都将视图中的数据项都完整的展示出来，有些数据项可能只展示了一部分，这里就需要设置偏移量来使我们的滚动贴合原生滚动的效果。可以看到图中的renderOffset就是偏移量，计算方式就是 滚动偏移量 减去 视图起始位置元素距离顶部的高度 ，所以为了设置偏移量，还需要设置一个包裹元素来包裹所渲染的列表项，我们的偏移量就加在这个包裹元素上，我们一般可以使用translateY来设置。不定高相比定高，不定高就在渲染前设置一个预估高度，这个高度指的是每个数据项的最小高度，然后再渲染出来列表元素后获取每一个元素的实际高度，最后按照定高的逻辑进行重新渲染。总结对于虚拟列表，主要思路就这些，我参照其他优秀博主的方案实现了一个React版的虚拟滚动，主要功能和问题如下：自定义的滚动条支持无限加载，滚动到底部再添加新数据10万+的数据，定高和非定高都可以流畅滚动 百万级数据快速拖滚动条会卡顿，定高的不会，主要是非定高的需要重新计算高度，所以时间都消耗在计算上了。数据量超过10万的情况快速将滚动条从顶部拖动到底部可能会出现滚动不到最后一项的bug，这点还没解决。对于列表项中有图片的情况，这个场景比较少见，所以暂时没有实现，实现思路的话也是和非定高一样，只是需要动态监听每一个列表项的高度变化，然后再进行更新。源码代码示意参考本文只是简略说了下思路，详细的可以看如下文章新手也能看懂的虚拟滚动实现方法react-virtualized

koa+node+redis实现注册图片验证码功能
type: item
date: 2022-08-20T15:51:26+00:00
description: koa+node+redis实现注册图片验证码功能基本思路设计图片验证码获取接口根据客户端唯一key将生成的验证码保存在redis中注册验证，根据key来获取验证码再和用户输入的验证码进行对比校验。安装redis这里我是使用的docker进行安装docker pull redisdocker run --restart=always --log-opt max-size=100m --log-opt max-file=2 -p 16380:6379 --name myredis -v /root/redis/redis.conf:/etc/redis/redis.conf -v /root/redis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes --requirepass 151545上述文件目录请自行创建 最后一个参数是设置密码，保证远程连接的安全性连接redisnode中使用到了redis这个库npm install redis -Sconst redis = require("redis"); const config = require("./config"); const client = redis.createClient({ socket: { port: config.REDIS_PORT, host: config.APP_HOST, }, password: config.REDIS_PASSWORD, }); // 监听连接事件 client .connect() .then(() => { console.log("redis连接成功"); }) .catch((err) => { console.log(err || "redis连接出错"); }); // 操作方法封装 get set function setString(key, value, expire) { return new Promise((resolve, reject) => { client .set(key, value) .then((res) => { if (expire) { client.expire(key, expire); } console.log("设置成功"); resolve(res); }) .catch((err) => { console.log("设置失败"); reject(err); }); }); } function getString(key) { return new Promise((resolve, reject) => { if (key) { client .get(key) .then((res) => { resolve(res); }) .catch((err) => { console.log(`获取${key}失败`); reject(err); }); } }); } module.exports = { setString, getString, }; 验证码获取接口首先验证码的生成我们选择svg-captcha这个库代码如下：const captcha = require("svg-captcha"); function createCaptcha(config = {}) { return captcha.create({ size: 4, ignoreChars: "0o1iIl", // 避免生成容易混淆的字符 noise: 3, color: true, background: "#f4f4f4", fontSize: 60, ...config, }); } module.exports = { createCaptcha, };验证码接口：const { createCaptcha } = require("../utils/createCaptcha"); const { setString } = require("../app/redis"); class CaptchaController { async create(ctx) { // 获取用户传递过来的唯一key const { sid: captchaKey } = ctx.request.body; // 生成验证码 const code = createCaptcha(); // 存储在redis中 设置过期事件 过期取出来的值为null await setString(captchaKey, code.text, 60 * 3); // 设置相应头 ctx.type = "im…

React-router实现路由拦截
type: item
date: 2022-03-09T08:40:00+00:00
description: 路由拦截在 Vue 中有现成的 API，所以在 Vue 中实现起来并不复杂。但是 React-router 中并没有这个功能，作者的目的是为了更加的灵活，鼓励我们自己去实现这个功能。当然思路很简单，我的思路就是在每个路由组件外层包裹一层组件进行判断。本次路由使用的是v6版本，与之前的版本有很大的改动，所以请事先去了解这些变化。talk is cheap show me my code首先基本目录如下：在App.jsx中导入路由配置：import { Link } from "react-router-dom"; import { Route, Routes, useRoutes } from "react-router-dom"; import "./App.css"; import routes from "./router"; function App(props) { const elements = useRoutes(routes); return ( 跳转 router1 | router2 {elements} ); } export default App; router.jsx内容如下：/* * @Date: 2022-01-23 20:15:56 * @LastEditors: zhangheng * @LastEditTime: 2022-03-09 16:18:35 */ import React, { lazy, Suspense, ComponentType } from "react"; import type { RouteObject } from "react-router-dom"; import Authorized from "./authorized"; import NoAuthor from "@/pages/403"; const routes: RouteObject[] = [ { path: "/router1", element: () => import("@/pages/router1"), }, { path: "/router2", element: () => import("@/pages/router2"), }, { path: "/403", element: <NoAuthor />, }, ]; type lazyPropsType = () => Promise<{ default: ComponentType }>; interface propsType { importRoute: lazyPropsType; } //定义自动包裹Suspense函数 function LazyElement(props: propsType) { return function () { const { importRoute } = props; const LazyComponent = lazy(importRoute); return ( <Suspense fallback={ 路由懒加载... }> <LazyComponent /> </Suspense> ); }; } // 处理routes 如果element是懒加载，要包裹Suspense,这里还自动包了一层懒加载组件 function dealRoutes(routesArr: RouteObject[]) { if (routesArr && Array.isArray(routesArr) && routesArr.length > 0) { routesArr.forEach((route) => { if (route.element && typeof route.element == "function") { const importRoute = route.element as lazyPropsType; route.element = ( <Authorized component={LazyElement({ importRoute })} include={["/router1", "/router2"]} /> ); } if (route.children) { dealRoutes(route.children); } }); } } dealRoutes(routes); export default routes; authorized.tsx鉴权组件，很简洁的：/* * @Date: 2022-03-09 11:49:30 * @LastEditors: zhangheng * @LastEditTime: 2022-03-09 16:17:1…

React使用动画
type: item
date: 2022-03-07T13:58:53+00:00
description: React使用动画我们在开发中可以使用一些动画来控制组件消失和隐藏的过渡动画，增加用户交互的体验。React社区提供了一个库react-transition-group来完成过渡动画。使用网址参考安装方式：//npm npm install react-transition-group -S //yarn yarn add react-transition-group -D这个库可以帮助我们实现组件的入场和离场动画，实际上就是为组件添加类名，根据不同的时刻，我们可以就此来设置过渡动画。该库体积很小，主要包含四个组件：Transition， CSSTransition， SwitchTransition ，TransitionGroupTransition该组件是一个与平台无关的组件，不一定要结合css，如果要结合css可以使用CSSTransition。这个组件常用于组件的挂载和卸载的过渡动画，该组件默认情况下Transition组件不会改变它呈现的组件的行为，它只跟踪组件的enter和exit状态。这取决于你赋予的状态效果。例如，当组件进入或退出时，我们可以向它添加style样式，以下是一个案例：import React, { PureComponent } from "react"; import { Transition } from "react-transition-group"; const duration = 300; const defaultStyle = { transition: `opacity ${duration}ms ease-in-out`, opacity: 0, }; const transitionStyles = { entering: { opacity: 1 }, entered: { opacity: 1 }, exiting: { opacity: 0 }, exited: { opacity: 0 }, }; export default class transitionDemo extends PureComponent { render() { return ( <Transition in={this.props.in} timeout={duration}> {(state) => ( I'm a fade Transition! )} </Transition> ); } }import React, { useState } from "react"; import CSSTransitionDemo from "./CSSTransitionDemo"; import SwitchTransitionDemo from "./SwitchTransitionDemo"; import TransitionGroupDemo from "./TransitionGroupDemo"; import TransitionDemo from "./transitionDemo"; function App() { const [show, setShow] = useState(false); return ( <TransitionDemo in={show} timeout={500} /> setShow(!show)}>点击隐藏 ); } export default App; 我们可以控制in的值来控制为组件赋予状态首先我们初始化in的值为false，那么组件内容默认不会显示，即处于exited状态，那么style设置的样式透明为0，就不会显示。当我们点击按钮，组件进入entering状态，当500ms（timeout）后组件处于entered状态。再次点击按钮组件处于exiting状态，延时之后组件就回归exited状态了。本质上就是在不同的状态添加不同的style样式。CSSTransition该组件在组件切换的不同时刻为组件添加类名，我们可以按照这些类名来给组件添加动画。该组件在执行的额过程中有三个状态，分别为：appear，enter，exit每个状态对应一组类名：appear状态： -appear，-enter，-exitenter状态：-appear-active，-enter-active，-exit-activeexit状态：-appear-done，-enter-done，-exit-done组件常用的属性in 触发进入或者退出状态。当为true的时候组件触发进入状态，会为组件添加class类名 -enter，-enter-acitve，动画结束后移除之前的两个class，并添加-enter-done类名。当为false的时候组件触发离开状…

React中书写css代码
type: item
date: 2022-03-07T13:57:45+00:00
description: React中书写css代码React本身就是一个支持组件化的框架，但是React并没有对css处理好组件化。相较于Vue，Vue这个一块就处理的很好，css放置在style标签中，而且可以设置scope作用域，防止样式覆盖，也可以设置css预处理器。在组件化开发中我们需要书写局部的css，全局的css，动态的css(根据状态来设置css)。React中书写css就没有那么的方便，下面介绍一些在React中css的书写方式。内联样式官方推荐的一种写法。export default class componentName extends Component { render() { return ( 你好 世界 ); } }css中短横线链接的属性需要转为小驼峰的形式来书写，以对象的形式书写css样式，同时可以引入state中的属性来动态引入属性。当然缺点很明显，如果需要书写大量的代码，那这种方式一定不方便，没有代码提示等。另外一些样式也不能书写，伪元素等选择器是不能书写的。普通的css引入我们将css单独写在一个文件中，在引入到文件中。import React, { Component } from "react"; import './index.css' export default class componentName extends Component { render() { return ( 你好 世界 ); } }但这样引入的css没有组件之间的作用域，是作用在全局的，属于全局css，会相互影响。所以适合写一些基本的全局样式。css module 写法该方法不是React自带的方法，只要在类似webpack配置打包环境下都可以使用。如果在其他项目中使用我们需要自己配置，配置webpack.config.js中的属性modules:true等。React脚手架已经配置了css modules的配置。我们书写的.css/.less/.scss都需要修改为.module.css/.module.less/.module.scss。引入方式，需要一个变量来接受，className需要以{style.className}形式来书写import React, { Component } from "react"; import style from './index.module.css' export default class componentName extends Component { render() { return ( 你好 世界 ); } }这样就避免了作用域问题，渲染出来的className如下：当然缺陷也有，比如引入的类名不能是短横线连接，不方便修改属性。但这方法也是不错的css书写方式。CSS in JS通过js来增强React中css书写的一种方式。当然我们学些这个方式前先了解下ES6的一种语法，ES6标签模板字符串我们使用该方法需要引入第三方库，这里推荐以下几个库：styled-componentsemotionglamorous我们以styled-components这个库来演示，这个库在社区中是很流行的。安装方式：//npm方式 npm install styled-components -S //yarn方式 yarn add styled-components该库本质上是调用函数来创建一个组件，组件会添加不会重复的class。该方式支持css预处理器的嵌套写法，支持css的各类写法。基本用法：import styled from "styled-components";//创建一个组件 const TitleWrapper = styled.div` color: yellow; .title { background-color: tomato; &::after { content: "---aaa"; } } `;这个函数中根元素为div，其中的样式只作用于该组件中export default class CssInJS extends Component { state = {color:'pink'} render() { return ( <TitleWrapper> 我是谁 hahah </TitleWrapper> ); } } 渲染结果：props,attr属性props可以穿透，直接作用在元素上const InputWrapper = styled.input` background-color: red; `; export default class CssInJS extends Component { state = {color:'pink'} render…

Redux的使用
type: item
date: 2022-03-07T13:56:11+00:00
description: Redux的使用纯函数函数式编程中有一个概念就是纯函数，JS中也有这个概念。在React中，纯函数的概念非常重要。定义：如果一个函数满足以下条件就可以称之为纯函数。此函数在相同的输入值时，需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关，也和由I/O设备产生的外部输出无关。就是输入值相同的时候，你无论调用该函数多少次输出的值都是一样的。该函数不依赖使用全局变量，常量是可以的。不会修改程序的状态，或者不能引起副作用。即不能影响输出值以外的内容。案例：//纯函数 function (a, b) { return a+b; } //不是纯函数,当然你可以把let改为const那就称之为纯函数了，因为foo成为常量了，是无法被修改的 let foo = 1; function (a) { return foo+a; }总之纯函数的出现是为了时函数不那么复杂，更容易调试和组合。React中之所以要求函数组件或class组件都要想纯函数一样是为了保护props不被修改。Redux概念目前使用JS开发的程序已经越来越复杂了，所以我们需要管理的状态也越来越多和复杂。我们在加入了组件化开发模式后需要对每个组件的状态进行合理的管理，有时还需涉及状态共享。所以为了更好的管理这些状态，我们可以使用redux这个状态管理库来管理我们的状态。Redux就是一个帮助我们管理State的容器：Redux是JavaScript的状态容器，提供了可预测的状态管理，使得状态更加可控和追踪。Redux并不依赖与React，还可以和Vue一同使用，本身体积很小（2kb左右）。首先看看React的工作原理：由上图可以看出Redux主要由三个核心部份组成action， store， reducer。核心概念之store该模块是把state和action和reducer联系起来，引入reducer文件然后向外暴露store对象。我们可以通过store获得state和派发action。核心概念之actionRedux要求所有修改数据的操作都要通过action来派发，action一般是一个JS对象，用来描述此次更新的type和content。核心概念之reducer该模块就是将state和action联系起来，reducer就是一个纯函数，用于接收action来生成新的state。简单案例展示，单独使用redux//导入redux const redux = require("redux"); //初始state const initialState = { counter: 0, }; //创建一个reducer function reducer(state = initialState, action) { switch (action.type) { case "INCREMENT": return { ...state, counter: state.counter + 1 }; case "DECREMENT": return { ...state, counter: state.counter - 1 }; case "ADD_NUM": return { ...state, counter: state.counter + action.num }; case "SUB_NUM": return { ...state, counter: state.counter - action.num }; default: return state; } } //store const store = redux.createStore(reducer); //action const action1 = { type: "INCREMENT" }; const action2 = { type: "DECREMENT" }; const action3 = { type: "ADD_NUM", num: 5 }; const action4 = { type: "SUB_NUM", num: 12 }; //订阅store修改 store.subscribe(() => { console.log('state发送了改变'); console.log(store.getState()); }) //派发action store.dispatch(action1); store.dispatch(action2); store.dispatch(action3); store.dispatch(action4);输出结果展示：state发送了改变 { counter: 1 } state发送了改变 { counter: 0 } state发送了改变 { co…

React的setState详解
type: item
date: 2022-03-07T13:54:45+00:00
description: React的setState详解我们使用React开发的时候，需要修改state中的数据，而我们并不能通过直接修改state中的数据来 实现界面的更新。当然用过Vue的都知道，Vue有自己的响应式系统，能够知道哪些数据发生了改变并自动更新视图。setState的基本使用React需要通过setState这个API来进行视图的重新渲染。这个函数是挂载在Component的原型上供我们使用。另外我们还要遵循一个原则就是永远都不要直接修改state中的属性值，通过setState来修改也是。****如：//这样修改是不会触发组件的更新 this.state.counter += 1; //正确的方式如下 this.setState({ counter:this.state.counter+1 })setState的更新setState是异步更新的我们通过setState修改数据之后是不能立马获取改变后的值的。//counter初值为1 this.setState({ counter:this.state.counter+1 }) console.log(this.state.counter);//1关于为什么setState是异步更新的在官方的github上有解释 链接我们每次调用setState来更新时，视图同步更新，但是多次调用就可能使得视图更新频繁，影响性能，所以设计为异步更新方式是为了批处理更新状态，将多次的更新合并成一次更新。另外如果同步更新state而没有及时执行render，那么state中的数据就和props数据不同步。当然我们也可以获取异步更新后的记结果，setState第二个参数可以传入一个回调函数，在这个回调函数中就可以获取异步更新后的结果。this.setState({ counter:this.state.counter+1 }, () => { console.log(this.state.counter) })setState是同步更新的在某些情况下我们可以让setState同步更新。把函数放在计数器中，设置时间为0那就是同步执行， 另外放置在原生的事件执行的回调函数也是同步执行的。setTimeout(() => { //初始为1 this.setState({ counter:this.state.counter+1 }) console.log(this.state.counter) //2 }, 0)当然整体上执行还是异步执行。所以我们这里可以做个总结：当我们在组件的生命周期或者React的合成事件中执行setState，其是异步执行的。当我们在计数器中执行或者原生的回调函数执行setState，其是同步执行的。setState是如何执行的找到定义方法的地方：Component.prototype.setState = function(partialState, callback) { //下面主要是校验和警告 invariant( typeof partialState === 'object' || typeof partialState === 'function' || partialState == null, 'setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.', ); //这里是关键，这个updater是每个类组件都拥有的方法 this.updater.enqueueSetState(this, partialState, callback, 'setState'); };点击进入updater：function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; }找到如下对象：****主要是调用了enqueueSetState这个方法，注意eventTime和lane这两个常量，点进后面的执行函数，这个函数主要…

React Hooks 使用介绍
type: item
date: 2022-03-07T13:51:00+00:00
description: React Hooks 使用介绍Hook 是React16.8新增的特性，可以说是扩展了函数式组件的各项功能。有了这个功能，函数式组件和类式组件在实现功能上就没有什么差异了，而且使用hook后，能使代码更加的简洁，代码也更具复用性了。useState以一个计数器来介绍：import React, {useState} from 'react' function Counter() { const [count, setCount] = useState(0); return ( 当前计数{count} setCount(count + 1)}>+1 setCount(count - 1)}>-1 ); }上面的组件就实现了一个计数器，点击按钮就会加减，hook使的函数组件有了自己的状态。**useState就是一个hook。**参数：初始化值，不设置就为undefined，也可以可以传入数组对象，另外还可以传入一个带有返回值的函数。**返回值：数组，包含两个元素，一般我们简化书写会使用数组解构语法。第一个元素使当前状态的值（第一次调用为初始化的值）第二个元素为设置状态值的函数。比如，上面的例子count的初始值为0 ，可以通过setCount来重新设置count的值，而且也会触发组件的创新渲染。我们可以在组件中多次使用这个hook来创建多个变量。****另外我们多次调用setXxx函数来修改对应状态的时候，其实也和类组件中的setState基本一样： //会被合并成一次执行 setCount(count+10); setCount(count+10); setCount(count+10); setCount(count+10); //不会被合并 setCount((prev) => prev + 10); setCount((prev) => prev + 10); setCount((prev) => prev + 10); setCount((prev) => prev + 10); setCount((prev) => prev + 10); 可以看到使用hook是多么的清爽。比新的一年换上新内裤还爽~~。那么接下来介绍第二个hook。useEffect函数组件自身没有生命周期这类函数，我们要实现这个功能就可以使用这个hook现在演示一个需求，页面的title总是显示counter的数字。****首先使用类组件进行实现：class ChangeTitle extends React.Component { constructor(props) { super(props); this.state = { count: 0, }; } componentDidMount() { document.title = this.state.count; } componentDidUpdate() { document.title = this.state.count; } render() { return ( 类组件当前标题计数:{this.state.count} this.setState({ count: this.state.count + 1 })}> +1 ); } }可以看到更改标题的操作有重复的部分。****函数式组件：function HookChangetitle() { const [count, setCount] = useState(0); //模拟生命周期 useEffect(() => { document.title = count; }); return ( 函数当前计数{count} setCount(count + 1)}>+1 setCount(count - 1)}>-1 ); }useEffect这个hook可以告知React在渲染好组件后执行某些操作。该hook要求传入一个回调函数，在React执行完DOM更新操作后就会回调这个函数。默认情况下，无论是第一次渲染还是之后的每次更新都会执行一次这个回调函数。清除effect我们需要在组件卸载的时候清除componentWillUnmount中进行清除。而useEffect为我们可以这样做，在我们传入的回调函数中，我么们可以指定一个返回值，返回一个回调函数。这是useEffect可选清除机制，每个useEffect都可以返回一个清除函数。 useEffect(() => { xxx return () => { console.log('清除了'); } });React会在组件每次更新和卸载的时候执行清除操作。而且useEffect也可以多次调用，我们可以根据代码用途来分开调用，使得代码更加明朗。useEffect性能优化某些代…

React-router V6 使用
type: item
date: 2022-02-15T14:01:00+00:00
description: 明明还没搞懂v5 ，v6就出了，学不动了哇。这里总结下v6版本的简要使用。[v5版本链接]()v6版本官网链接开始这里我们已经创建了一个空的React项目安装yarn add react-router-domBrowserRouter这里我们选择BrowserRouter模式import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import App from "./App"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode>, document.getElementById("root") ); Link进入App.jsx我们添加一些链接/* * @Date: 2022-02-15 19:01:38 * @LastEditors: zhangheng * @LastEditTime: 2022-02-15 19:18:48 */ import React, { memo } from "react"; import { Link } from "react-router-dom"; export default memo(function index() { return ( hello react router router1 | router2 ); }); 此时应用展示如下：当我们点击链接后url就会改变，当然页面不会有任何的变化。渲染路由组件我们创建两个组件：在main.tsx文件中引入，再使用Route和Routes：这里注意我们在v5是使用Switch组件包裹路由组件，v6则是移除了Switch，转而使用Routes了。import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import App from "./App"; import Router1 from "./pages/router1"; import Router2 from "./pages/router2"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path="/" element={<App />}></Route> <Route path="/router1" element={<Router1 />}></Route> <Route path="/router2" element={<Router2 />}></Route> </Routes> </BrowserRouter> </React.StrictMode>, document.getElementById("root") );嵌套路由上述的案例我们点击后就只会显示对应组件的信息，破坏了布局，这时我们想在App中展示路由组件可以按照如下修改：import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import App from "./App"; import Router1 from "./pages/router1"; import Router2 from "./pages/router2"; ReactDOM.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path="/" element={<App />}> <Route path="/router1" element={<Router1 />}></Route> <Route path="/router2" element={<Router2 />}></Route> </Route> </Routes> </BrowserRouter> </React.StrictMode>, document.getElementById("root") );当然我们需要在App组件中去渲染要展示的组件：使用Outlet组件/* * @Date: 2022-02-15 19:01:…