react PC端项目构建TS,react@18.2.0+antd+vite+axios+redux+sass+ts 完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88922625
react PC端项目构建,react@18.2.0+antd+vite+axios+redux+sass完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88922569
react移动端项目构建TS,react@18.2.0+react-vant+vite+axios+redux+sass+ts完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88917557
react移动端项目构建,react@18.2.0+react-vant+vite+axios+redux+sass完整版代码下载:
https://download.youkuaiyun.com/download/randy521520/88917543
一、react简介
React 是由 Facebook 开发的一款用于构建用户界面的 JavaScript 库。它主要专注于构建单页面应用程序(SPA)中的用户界面,采用组件化的开发方式,使得开发者能够更轻松地构建交互式、动态的前端界面。本文主要将React@18.2.0版本,由于React 官方文档通常更倾向于推荐函数组件和 Hooks 的使用,这这里主要介绍函数组件
以下是 React 的一些主要特点和优势:
1.组件化开发:React 鼓励开发者将界面拆分成独立的组件,每个组件负责管理自己的状态和 UI 显示。这种组件化开发方式使得代码更易于维护、复用和测试。
2.虚拟 DOM:React 使用虚拟 DOM 技术来提高性能。通过在内存中维护一颗虚拟 DOM 树,React 能够高效地比对前后两次状态的差异,并只更新必要的部分,从而减少对实际 DOM 的操作,提升页面渲染性能。
3.单向数据流:React 遵循单向数据流的原则,数据的流动是单向的,从父组件传递给子组件。这种数据流清晰明了,易于追踪数据的变化,减少了出现 bug 的可能性。
4.JSX 语法:React 使用 JSX 语法,允许在 JavaScript 代码中编写类似 HTML 的标记,使得 UI 代码更加直观和易读。
5.生命周期方法:React 提供了一系列生命周期方法,允许开发者在组件的不同阶段执行特定的逻辑,例如组件挂载时、更新时、卸载时等。
6.社区支持:React 拥有庞大的开发者社区和生态系统,提供了丰富的第三方库和工具,帮助开发者更高效地构建 React 应用。
二、react Hook之useState
useState管理组件状态,实现双向数据绑定,类似class组件种的this.state,用法useState(value);
import {useState} from "react";
const Home = () => {
let [count, setCount] = useState(0);
const onCountChange = () => {
count++;
setCount(count);
}
return (<>
<div onClick={onCountChange}>改变Count</div>
<div>页面渲染次数:{count}</div>
</>)
}
export default Home;
三、react Hook之useEffect
useEffect用于在函数组件中执行副作用操作。副作用是指除了渲染界面之外对组件外部环境产生影响的操作,比如数据获取、订阅事件、手动操作 DOM 等,类似class组件种的生命周期。接受两个参数fn、dependencies,用法useEffect(fn,dependencies);类似的组件还有useLayoutEffect、useInsertionEffect。useLayoutEffect是useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。useLayoutEffect 可能会影响性能。尽可能使用 useEffect;useInsertionEffect是为 CSS-in-JS 库特意打造的,除非你正在使用 CSS-in-JS 库并且需要注入样式,否则应该使用 useEffect 或者 useLayoutEffect。
1.组件生命周期之组件初始化
import {useEffect} from "react";
const Home = () => {
useEffect(() => {
console.log('组件初始化')
}, []);
return (<></>)
}
export default Home;
2.组件生命周期之组件卸载
import {useEffect} from "react";
const Home = () => {
useEffect(() => {
return ()=>{
console.log('组件卸载')
}
}, []);
return (<></>)
}
export default Home;
3.组件生命周期之组件props改变
import React, {useEffect, useState} from "react";
/******************子组件**********************/
const _Content = (props) => {
useEffect(() => {
console.log('组件props改变')
}, [props]);
return (<div>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
let [count, setCount] = useState(0);
const onCountChange = () => {
count++;
setCount(count);
}
return (<>
<div onClick={onCountChange}>改变Count</div>
<div>页面渲染次数:{count}</div>
<Content content={count}></Content>
</>)
}
export default Home;
4.组件生命周期之组件state改变
import {useEffect, useState} from "react";
const Home = () => {
let [count, setCount] = useState(0);
const onCountChange = () => {
count++;
setCount(count);
}
useEffect(()=>{
console.log('count改变')
},[count])
return (<>
<div onClick={onCountChange}>改变Count</div>
<div>页面渲染次数:{count}</div>
</>)
}
export default Home;
四、react Hook之useCallback
useCallback在多次渲染中缓存函数,接受两个参数fn、dependencies,用法useCallback(fn,dependencies);
- fn:缓存的函数。此函数可以接受任何参数并且返回任何值。只会在初次渲染时返回此函数,当下一次渲染时,如果dependencies相比于上一次渲染时没有改变,那么将会返回相同的函数
- dependencies:是否更新 fn 的所有响应式值的一个列表。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。
1.使用场景一:将函数传递给子组件,跳过组件的重新渲染。常和memo一起优化性能 - 不使用useCallback,向子组件传递函数;每次点击onContentClick时,子组件都会重新渲染,但是onContentClick没变化,子组件应不会重新渲染,这是因为父组件重新渲染时onContentClick始终是不同的,导致memo性能优化没生效
import React, {useState} from "react";
/******************子组件**********************/
const _Content = ({onClick})=>{
console.log('子组件重新渲染')
return (<div onClick={onClick}>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = ()=>{
let [count,setCount] = useState(0);
const onContentClick = ()=>{
count++;
setCount(count)
}
return (<>
<Content onClick={onContentClick}></Content>
<div>父组件渲染次数:{count}</div>
</>)
}
export default Home;
- 使用useCallback,向子组件传递函数;使用useCallback后父组件重新渲染了,子组件未重新渲染,达到了优化效果;如果在useCallback中传入[content],那么content变化,onContentClick 也会改变,子组件就会重新渲染
import React, {useCallback, useState} from "react";
/******************子组件**********************/
const _Content = ({onClick})=>{
console.log('子组件重新渲染')
return (<div onClick={onClick}>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = ()=>{
let [count,setCount] = useState(0);
const onContentClick = useCallback(()=>{
count++;
setCount(count)
},[])
return (<>
<Content onClick={onContentClick}></Content>
<div>父组件渲染次数:{count}</div>
</>)
}
export default Home;
2.使用场景二:防止频繁触发Effect ,假如有这样的一个功能:需要在一个组件连接一个Websocket,每次Websocket的id改变,就断开之前的连接重新连接
- 不使用useCallback,由图可以看出roomId没改变,Websocket会反复连接端口
import React, {useCallback, useEffect, useState} from "react";
/******************子组件**********************/
const _Content = ({count, roomId}) => {
const createWebsocketOptions = () => {
return {
serverUrl: 'https://localhost:1234',
roomId: roomId
};
};
useEffect(() => {
const options = createWebsocketOptions();
console.log(options);
return () => {
console.log(`断开之前的连接${options.roomId}`)
}
}, [createWebsocketOptions]);
return (<div>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
let [count, setCount] = useState(0);
let [roomId, setRoomId] = useState(1000);
const onCountChange = () => {
count++;
setCount(count);
}
const onRoomIdChange = () => {
roomId++;
setRoomId(roomId);
}
return (<>
<div onClick={onCountChange}>改变Count</div>
<div onClick={onRoomIdChange}>改变RoomId</div>
<div>页面渲染次数:{count}</div>
<div>Websocket ID:{roomId}</div>
<Content content={count} roomId={roomId}></Content>
</>)
}
export default Home;
- 使用useCallback,只有roomId改变,Websocket才会端口旧的,连接新的;只是示例说明useCallback使用场景,直接使用useEffect来处理roomId的改变也是可以的
import React, {useCallback, useEffect, useState} from “react”;
import React, {useCallback, useEffect, useState} from "react";
/******************子组件**********************/
const _Content = ({count, roomId}) => {
const createWebsocketOptions = useCallback(() => {
return {
serverUrl: 'https://localhost:1234',
roomId: roomId
};
},[roomId]);
useEffect(() => {
const options = createWebsocketOptions();
console.log(options);
return () => {
console.log(`断开之前的连接${options.roomId}`)
}
}, [createWebsocketOptions]);
return (<div>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
let [count, setCount] = useState(0);
let [roomId, setRoomId] = useState(1000);
const onCountChange = () => {
count++;
setCount(count);
}
const onRoomIdChange = () => {
roomId++;
setRoomId(roomId);
}
return (<>
<div onClick={onCountChange}>改变Count</div>
<div onClick={onRoomIdChange}>改变RoomId</div>
<div>页面渲染次数:{count}</div>
<div>Websocket ID:{roomId}</div>
<Content content={count} roomId={roomId}></Content>
</>)
}
export default Home;
3.使用场景三:优化自定义 Hook,建议将hook返回的任何函数包裹在 useCallback 中,可以保证使用者在需要时能够优化自己的代码。
import {useCallback,useState} from "react";
function useRouter() {
let [params, setCountParams] = useState({});
const navigate = useCallback((url) => {
setCountParams({ type: 'navigate', url });
}, [count]);
const goBack = useCallback(() => {
setCountParams({ type: 'back' });
}, [count]);
return {
navigate,
goBack,
};
}
4.使用场景四:从记忆化回调中更新 state,不使用useCallback包裹,每次组件重新渲染时都会创建一个新的函数,会导致不必要的性能损失;使用useCallback,避免不必要的函数重新创建
import {useCallback, useState} from "react";
const Home = () => {
let [count, setCount] = useState(0);
const onCountChange = useCallback(() => {
count++;
setCount(count);
},[count])
return (<>
<div onClick={onCountChange}>改变Count</div>
<div>页面渲染次数:{count}</div>
</>)
}
export default Home;
五、react Hook之useMemo
useMemo用于在函数组件中进行性能优化。它可以缓存计算结果并在依赖项发生变化时重新计算,从而避免不必要的重复计算,用法useMemo(fn,dependencies);
1.使用场景一:避免代价昂贵的重新计算
- 不使用useMemo,由图可以看出每次count改变,都会重复调用filterTodos
import {useEffect, useState} from "react";
const Home = () => {
let [count, setCount] = useState(0);
let [todo, setTodo] = useState([1, 2, 3, 4, 3, 5, 4]);
const onContentChange = () => {
count++;
setCount(count)
}
const onTodoChange = () => {
todo.push(Math.floor(Math.random() * 17) + 3);
setTodo([...todo])
}
const filterTodos = (todos) => {
return [...new Set(todos.filter(element => element > 2).sort())];
}
const visibleTodos = filterTodos(todo);
useEffect(() => {
console.log('过滤后的值:', visibleTodos)
}, [visibleTodos]);
return (<>
<div onClick={onContentChange}>改变content</div>
<div onClick={onTodoChange}>改变todo</div>
<div>父组件渲染次数:{count}</div>
<div>todo值:{todo.join(',')}</div>
<div>visibleTodos值:{visibleTodos.join(',')}</div>
</>)
}
export default Home;
- 使用useMemo,count改变不会影响visibleTodos计算
import {useEffect, useMemo, useState} from "react";
const Home = () => {
let [count, setCount] = useState(0);
let [todo, setTodo] = useState([1, 2, 3, 4, 3, 5, 4]);
const onContentChange = () => {
count++;
setCount(count)
}
const onTodoChange = () => {
todo.push(Math.floor(Math.random() * 17) + 3);
setTodo([...todo])
}
const filterTodos = (todos) => {
return [...new Set(todos.filter(element => element > 2).sort())];
}
const visibleTodos = useMemo(()=>filterTodos(todo),[todo]);
useEffect(() => {
console.log('过滤后的值:', visibleTodos)
}, [visibleTodos]);
return (<>
<div onClick={onContentChange}>改变content</div>
<div onClick={onTodoChange}>改变todo</div>
<div>父组件渲染次数:{count}</div>
<div>todo值:{todo.join(',')}</div>
<div>visibleTodos值:{visibleTodos.join(',')}</div>
</>)
}
export default Home;
2.场景二:避免组件的重新渲染
- 不使用useMemo,由图可以看出content组件的其他props改变,都会调用filterTodos
import React, {useEffect, useState} from "react";
/******************子组件**********************/
const _Content = ({count,todos}) => {
const filterTodos = (todos) => {
return [...new Set(todos.filter(element => element > 2).sort())];
}
const visibleTodos = filterTodos(todos);
useEffect(() => {
console.log('过滤后的值:', visibleTodos)
}, [visibleTodos]);
return (<div>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
let [count, setCount] = useState(0);
let [todo, setTodo] = useState([1, 2, 3, 4, 3, 5, 4]);
const onContentChange = () => {
count++;
setCount(count)
}
const onTodoChange = () => {
todo.push(Math.floor(Math.random() * 17) + 3);
setTodo([...todo])
}
return (<>
<div onClick={onContentChange}>改变content</div>
<div onClick={onTodoChange}>改变todo</div>
<div>父组件渲染次数:{count}</div>
<Content count={count} todos={todo}></Content>
</>)
}
export default Home;
- 使用useMemo,由图可以看出content组件的其他props改变,不会调用filterTodos
import React, {useEffect, useMemo, useState} from "react";
/******************子组件**********************/
const _Content = ({count,todos}) => {
const filterTodos = (todos) => {
return [...new Set(todos.filter(element => element > 2).sort())];
}
const visibleTodos = useMemo(()=>filterTodos(todos),[todos]);
useEffect(() => {
console.log('过滤后的值:', visibleTodos)
}, [visibleTodos]);
return (<div>content页</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
let [count, setCount] = useState(0);
let [todo, setTodo] = useState([1, 2, 3, 4, 3, 5, 4]);
const onContentChange = () => {
count++;
setCount(count)
}
const onTodoChange = () => {
todo.push(Math.floor(Math.random() * 17) + 3);
setTodo([...todo])
}
return (<>
<div onClick={onContentChange}>改变content</div>
<div onClick={onTodoChange}>改变todo</div>
<div>父组件渲染次数:{count}</div>
<Content count={count} todos={todo}></Content>
</>)
}
export default Home;
六、react Hook之useRef、useImperativeHandle
useRef 是 React 提供的一个 Hook,主要用于在函数组件中存储可变值,并且在组件重新渲染时保持该值不变。useRef 返回一个可变的 ref 对象,其 current 属性可以被赋值为任何值,可搭配useImperativeHandle向父组件暴露的实例值,类似class组件种的React.createRef方式;需使用forwardRef
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";
/******************子组件**********************/
const _Content = (props,ref) => {
let [count, setCount] = useState(0);
useImperativeHandle(ref,()=>({
onCountChange: () => {
count++;
setCount(count);
}
}));
return (<div>
<div>页面渲染次数:{count}</div>
</div>)
};
const Content = React.memo(forwardRef(_Content))
/******************父组件**********************/
const Home = () => {
let ref = useRef();
let divRef = useRef()
const inputRef = useRef(null);
useEffect(() => {
console.log(ref.current); //组件挂载获取子组件暴漏出的ref示例
console.log(divRef.current); //组件挂载获取子div元素
inputRef.current.focus(); // 在组件挂载后使 input 元素获取焦点
}, []);
return (<>
<div ref={divRef} onClick={()=>ref.current?.onCountChange()}>改变Count</div>
<Content ref={ref}></Content>
<input ref={inputRef} type="text"/>
</>)
}
export default Home;
七、react Hook之useContext
useContext读取和订阅组件中的 context。用法:useContext(SomeContext),需要配合createContext,使用上下文 provider 包裹组件,为里面所有的组件指定一个上下文的值
import React, {useContext} from "react";
const HomeContext = React.createContext({});
/******************子组件**********************/
const _Content = () => {
const contentValue = useContext(HomeContext);
return (<div>{contentValue.form}</div>)
};
const Content = React.memo(_Content)
/******************父组件**********************/
const Home = () => {
const contentValue = {
form:'home页'
};
return (<HomeContext.Provider value={contentValue}>
<Content/>
</HomeContext.Provider>)
}
export default Home;
八、react Hook之useTransition
useTransition用于在渲染过渡期间对UI状态进行优化。它允许您将渲染工作分割成多个优先级较低的部分,以便浏览器可以在多个帧中完成这些工作,从而改善性能和用户体验。使用方法:const [isPending, startTransition] = useTransition();
- isPending:是否存在待处理的 transition。
- startTransition:函数,使用此方法将状态更新标记为 transition。
1.不使用useTransition,代码示有四个子组件TabButton、AboutTab、ContactTab、PostsTab,其中TabButton是用来切换tab的,AboutTab、ContactTab是无耗时任务组件,PostsTab是耗时任务组件,当从AboutTab切换到PostsTab再快速切换到ContactTab,会发现PostsTab也渲染了
import React, {useEffect, useState} from "react";
/******************子组件**********************/
const _TabButton = ({children, isActive, onClick}) => {
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
onClick();
}}>
{children}
</button>
)
}
const TabButton = React.memo(_TabButton);
/******************子组件**********************/
const _AboutTab = () => {
console.log("AboutTab被触发了");
useEffect(() => {
console.log("AboutTab被渲染");
}, []);
return (
<p>Welcome to my profile!</p>
);
}
const AboutTab = React.memo(_AboutTab);
/******************子组件**********************/
const _ContactTab = () => {
console.log("ContactTab被触发了");
useEffect(() => {
console.log("ContactTab被渲染");
}, []);
return (
<>
<p>
You can find me online here:
</p>
<ul>
<li>admin@mysite.com</li>
<li>+123456789</li>
</ul>
</>
);
}
const ContactTab = React.memo(_ContactTab);
/******************子组件**********************/
function SlowPost({index}) {
let startTime = performance.now();
while (performance.now() - startTime < 1) {
// 每个 item 都等待 1 毫秒以模拟极慢的代码。
}
return (
<li className="item">
Post #{index + 1}
</li>
);
}
const _PostsTab = () => {
console.log("PostsTab被触发了");
useEffect(() => {
console.log("PostsTab被渲染");
}, []);
let items = [];
for (let i = 0; i < 500; i++) {
items.push(<SlowPost key={i} index={i}/>);
}
return (
<ul className="items">
{items}
</ul>
);
};
const PostsTab = React.memo(_PostsTab);
/******************父组件**********************/
const Home = () => {
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
setTab(nextTab);
}
return (
<>
<TabButton isActive={tab === 'about'}
onClick={() => selectTab('about')}>
About
</TabButton>
<TabButton isActive={tab === 'posts'}
onClick={() => selectTab('posts')}>
Posts (slow)
</TabButton>
<TabButton isActive={tab === 'contact'}
onClick={() => selectTab('contact')}>
Contact
</TabButton>
<hr/>
{tab === 'about' && <AboutTab/>}
{tab === 'posts' && <PostsTab/>}
{tab === 'contact' && <ContactTab/>}
</>
);
}
export default Home;
2.使用useTransition,当从AboutTab切换到PostsTab再快速切换到ContactTab,会发现PostsTab不会渲染了
import React, {useEffect, useState, useTransition} from "react";
/******************子组件**********************/
const _TabButton = ({children, isActive, onClick}) => {
if (isActive) {
return <b>{children}</b>
}
return (
<button onClick={() => {
onClick();
}}>
{children}
</button>
)
}
const TabButton = React.memo(_TabButton);
/******************子组件**********************/
const _AboutTab = () => {
console.log("AboutTab被触发了");
useEffect(() => {
console.log("AboutTab被渲染");
}, []);
return (
<p>Welcome to my profile!</p>
);
}
const AboutTab = React.memo(_AboutTab);
/******************子组件**********************/
const _ContactTab = () => {
console.log("ContactTab被触发了");
useEffect(() => {
console.log("ContactTab被渲染");
}, []);
return (
<>
<p>
You can find me online here:
</p>
<ul>
<li>admin@mysite.com</li>
<li>+123456789</li>
</ul>
</>
);
}
const ContactTab = React.memo(_ContactTab);
/******************子组件**********************/
function SlowPost({index}) {
let startTime = performance.now();
while (performance.now() - startTime < 1) {
// 每个 item 都等待 1 毫秒以模拟极慢的代码。
}
return (
<li className="item">
Post #{index + 1}
</li>
);
}
const _PostsTab = () => {
console.log("PostsTab被触发了");
useEffect(() => {
console.log("PostsTab被渲染");
}, []);
let items = [];
for (let i = 0; i < 500; i++) {
items.push(<SlowPost key={i} index={i}/>);
}
return (
<ul className="items">
{items}
</ul>
);
};
const PostsTab = React.memo(_PostsTab);
/******************父组件**********************/
const Home = () => {
const [isPendingTab, startTransitionTab] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransitionTab(() => {
setTab(nextTab);
});
}
return (
<>
<TabButton isActive={tab === 'about'}
onClick={() => selectTab('about')}>
About
</TabButton>
<TabButton isActive={tab === 'posts'}
onClick={() => selectTab('posts')}>
Posts (slow)
</TabButton>
<TabButton isActive={tab === 'contact'}
onClick={() => selectTab('contact')}>
Contact
</TabButton>
<hr/>
{tab === 'about' && <AboutTab/>}
{tab === 'posts' && <PostsTab/>}
{tab === 'contact' && <ContactTab/>}
</>
);
}
export default Home;
九、react Hook之useDeferredValue
useDeferredValue用于延迟更新状态的值,以便在性能方面进行优化。它可以用于在某些情况下推迟对某个值的更新,以减少渲染的频率,从而提高性能。如下面代码:list组件经过大量的计算k,如果不使用useDeferredValue页面会出现明显的卡顿,使用useDeferredValue页面会变得更丝滑
import React, {useDeferredValue, useState} from "react";
/******************子组件**********************/
const _List = ({query})=>{
console.log('List render')
let k = 0
for (let i = 0; i <= 200000000; i += 1) {
k = i
}
return (
<ul>
<li>列表 {k} 文本: {query}</li>
<li>列表 {k} 文本: {query}</li>
<li>列表 {k} 文本: {query}</li>
<li>列表 {k} 文本: {query}</li>
<li>列表 {k} 文本: {query}</li>
</ul>
)
}
const List = React.memo(_List);
/******************父组件**********************/
const Home = () => {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
console.log("query:", query);
console.log("deferredQuery:", deferredQuery)
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)}/>
</label>
<List query={deferredQuery}/>
</>
);
};
export default Home;
十、react Hook之useReducer
useReducer向组件里面添加一个 reducer,可以更好地处理复杂的状态逻辑。相比于useState,useReducer 更适合管理具有复杂状态逻辑的组件,尤其是涉及多个子值或需要在更新时执行复杂逻辑的情况。使用方法:useReducer(reducer, initValue)
import {useReducer} from "react";
function reducer(state, action) {
if (action.type === 'incremented_age') {
return {
age: state.age + 1
};
}
throw Error('Unknown action.');
}
const Home = () => {
const [state, dispatch] = useReducer(reducer, { age: 42 });
return (
<>
<button onClick={() => {
dispatch({ type: 'incremented_age' })
}}>
Increment age
</button>
<p>Hello! You are {state.age}.</p>
</>
);
};
export default Home;
十一、react Hook之useSyncExternalStore
useSyncExternalStore 是一个可以订阅外部 store 的 React Hook。使用方法:useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
import {useSyncExternalStore} from "react";
/******************外部store**********************/
let nextId = 0;
let todos = [{ id: nextId++, text: 'Todo #1' }];
let listeners = [];
function emitChange() {
for (let listener of listeners) {
listener();
}
}
const todosStore = {
addTodo() {
todos = [...todos, { id: nextId++, text: 'Todo #' + nextId }]
emitChange();
},
subscribe(listener) {
listeners = [...listeners, listener];
return () => {
listeners = listeners.filter(l => l !== listener);
};
},
getSnapshot() {
return todos;
}
};
/******************组件**********************/
const Home = () => {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
return (
<>
<button onClick={() => todosStore.addTodo()}>Add todo</button>
<hr />
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</>
);
};
export default Home;
十二、react 组件
1.<Fragment></Fragment>、<></>:允许在不添加额外节点的情况下将子元素组合,用它包括的子元素无父节点
2.<StrictMode></StrictMode>:为组件启用严格模式
3.<Suspense fallback={}></Suspense>:处理异步组件操作的加载和错误
4.<Profiler id=“App” onRender={onRender}></Profiler>:编程式测量 React 树的渲染性能,onRender参数:id、phase、actualDuration、baseDuration、startTime、commitTime
十三、react Api
1.createContext(initValue):创建上下文,常与useContext配合使用
2.forwardRef(render):允许组件使用 ref 将 DOM 节点暴露给父组件,常与useRef、useImperativeHandle配合使用
3.lazy(load):能够让组件第一次被渲染之前延迟加载组件的代码,lazy(() => import(‘./MarkdownPreview.js’))
4.memo(SomeComponent, arePropsEqual?):允许你的组件在 props 没有改变的情况下跳过重新渲染,优化组件性能
5.startTransition():可以在不阻塞 UI 的情况下更新 state。