以下是React18版本的基础学习资源
点击我获取更多学习资源
1. …模板扩展符
import logo from './logo.svg';
import './App.css';
function App() {const imgData = {className: 'small',style: {wdith: '200px',height: '200px',backgroundColor: 'grey'}}return (<div className="App"><img src={logo}alt=""{...imgData}/></div>);
}export default App;
2. 模板扩展发嵌套使用
import './App.css';function Detail({content, active}) {return (<><p>{content}</p><p>状态:{active}</p></>)
}
function Article({title, detailData}) {return (<div><h2>{title}</h2><Detail {...detailData}></Detail></div>)
}
function App() {const articleData = {title: '标题111',detailData: {content: '内容111',active: true}}return (<><Article{...articleData}/></>);
}export default App;
3. 默认插槽
import './App.css';function List({children}) {return (<ul>{children}</ul>)
}
function App() {return (<><List><li>列表1</li><li>列表2</li></List></>);
}export default App;
4. 带名字插槽
import './App.css';function List({children, title, footer = <div>默认底部</div>}) {return (<><h2>{title}</h2><ul>{children}</ul>{footer}</>)
}
function App() {return (<><Listtitle="列表title"footer={<p>这是底部内容</p>}><li>列表1</li><li>列表2</li></List></>);
}export default App;
5. 子调用父
import './App.css';
import {useState} from 'react'
function Detail({onActive}) {const [status, setStatus] = useState(false) function handleClick() {setStatus(!status)onActive(status)}return (<div><button onClick={handleClick}>按钮</button><p style={{display: status ? 'block' : 'none'}}>Detail内容</p></div>)
}
function App() {function handleActive (status) {console.log(status)}return (<><Detail onActive={handleActive}></Detail></>);
}export default App;
6. useReducer作用
import './App.css';
import {useReducer, useState} from 'react'function countReducer(state, action) {switch(action.type) {case 'increment':return state + 1case 'decrement':return state - 1default:throw new Error()}
}
function App() {
const [state, dispatch] = useReducer(countReducer, 0)
const handleIncrement = () => dispatch({type: 'increment'})
const handleDecrement = () => dispatch({type: 'decrement'})return (<><button onClick={handleIncrement}>+</button><span>{state}</span><button onClick={handleDecrement}>-</button></>);
}export default App;
7. 父调用子 forwardRef, useImperativeHandle, useRef
import './App.css';
import { forwardRef, useImperativeHandle, useRef } from 'react'const Child = forwardRef(function(props, ref) {useImperativeHandle(ref, () => ({myFun: ()=> {console.log('子方法被调用')}}))return (<div>子组件</div>)
})
function App() {const childRef = useRef()const handleClick = () => {childRef.current.myFun()}return (<><Child ref={childRef} /><button onClick={handleClick}>调用子方法</button></>);
}export default App;
8. useEffect 作用
import './App.css';
import { useEffect, useState } from 'react'function App() {const [count, setCount] = useState(0)const [count2, setCount2] = useState(0)// 只要界面一变化,都会触发useEffect(() => {console.log('useEffect')return () => {// 当页面卸载执行,清理数据}})// useEffcet(() => {// return () => {// console.log('类似于componentWillUnmount,通常用于清除副作用');// }// }, [])// useEffcet(() => {
// console.log('类似于componentDidMount,通常在此处调用api获取数据')
// }, [])// useEffect useEffect 返回的是一个清理函数,//不能是async的promise,如果里面有异步执行,可以用以下方法// useEffect(() => {// console.log('useEffect')// return () => {// ;(async () => {// const data = await http.get('....')// })();// // 当页面卸载执行,清理数据// }// })//只有count才会触发// useEffect(() => {// console.log('useEffect')// }, [count])const handleClick = () => {setCount(count + 1)}const handleClick2 = () => {setCount2(count2 + 1)}return (<><button onClick={handleClick}>点击触发{count}</button><button onClick={handleClick2}>点击触发2{count2}</button></>);
}export default App;
9. useMemo缓存数据,避免重复计算
import './App.css';
import { useMemo, useState } from 'react'function DoSomeMath ({value}) {// 利用useMemo,可以缓存result,不会重新计算,但该函数依然会执行console.log('DosomeMathc执行了')const result = useMemo(() => {console.log('DosomeMathc2执行了')let result = 0for (let i = 0; i < 100000; i++) {result += value * 2}return result}, [value])return (<div>{result}</div>)
}function DoSomeMath2 ({value}) {// 外面变化,在value没变化的情况下,内部依然都要重新计算,不符合逻辑console.log('DosomeMathc执行了')let result = 0for (let i = 0; i < 100000; i++) {result += value * 2}return (<div>{result}</div>)
}
function App() {const [inputValue, setInputValue] = useState(5)const [count, setCount] = useState(0)return (<><p>count值为: {count}</p><buttononClick={() => setCount(count + 1)}>点击更新</button><br/><input type="number"value={inputValue}onChange={(e) => setInputValue(parseInt(e.target.value))} /><DoSomeMath value={inputValue} /></>);
}export default App;
10. memo 记忆组件和useCallback记忆函数
import './App.css';
import { memo, useCallback, useState } from 'react'function MyButton2 ({onClick}) {// 外部变化,每部都会被渲染一次console.log('mybutton被执行')return <button onClick={onClick}>子组件</button>
}const MyButton = memo(function ({onClick}) {// 外部变化,每部都会被渲染一次console.log('mybutton被执行')return <button onClick={onClick}>子组件</button>
})
function App() {const [count, setCount] = useState(0)const handleUpdate = () => setCount(count + 1)// app 每次重新渲染,handleClick都是一个新的函数,依然会触发子组件更新const handleClick2 = () => {console.log('handleClick被点击...')}
// 利用useCallback可以缓存住handleClick函数,成为记住组件const handleClick = useCallback( () => {console.log('handleClick被点击...')}, [])return (<><p>Count: {count}</p><button onClick={handleUpdate}>点击</button><br/><MyButton onClick={handleClick}></MyButton></>);
}export default App;
11. mobx
[https://blog.csdn.net/qq_53123067/article/details/129707090]
import { makeAutoObservable, autorun, reaction, runInAction } from "mobx";class Counter {constructor(){//makeAutoObservable的使用:makeAutoObservable就像加强版的makeObservable,默认情况下它将推断所有的属性,推断的规则为:所有的属性都成为observable,所有的方法都成为action,所有的计算属性都成为computed (计算属性接下来会讲到)。具体的使用如下:// 参数1:target让对象变成可观察// 参数2:排除属性和方法// 参数3:指定自动绑定thismakeAutoObservable(this, {}, {autoBind: true})}count = 1;increment(){this.count++}incrementAsync(){setTimeout(() => {runInAction(() => {this.count++})}, 1000);}decrement(){this.count--}reset(){this.count = 0}// 计算属性get doubleCount() {return this.count * 2}getDoubleCount() {return this.count * 2}
}const counter = new Counter()
// 监听counter变化, 初始化会执行一次
autorun(() => {console.log('监听count:', counter.count)},)// 监听数据的变化
//reaction类似于autorun,但可以更加精细地控制要跟踪的可观察对象,与autorun不同,reaction在初始化时不会自动运行。
// reaction接收两个参数,参数1:data函数,其返回值会作为第二个函数的输入;参数2:回调函数reaction(() => counter.count, (value, oldValue) => {console.log('count发生变化', value, oldValue)})
export default counter
app.js
import './App.css';
import { memo, useCallback, useState } from 'react'
import { observer } from 'mobx-react-lite'
import counter from './store/counter';
// import counter2 from './store/counter';function App() {return (<><h2>计数器案例</h2><div>点击次数:{counter.count}</div>{/* <div>点击次数:{counter2.count}</div> */}<div>计算属性次数:{counter.doubleCount}</div><button onClick={()=>counter.increment()}>加1</button><button onClick={()=>counter.decrement()}>减1</button><button onClick={()=>counter.incrementAsync()}>异步加1</button><button onClick={()=>counter.reset()}>重置</button></>);
}export default observer(App);
12. useEffect 第二个参数详解
- undefined: 任何状态改变都会执行
- 不是数组:报警
- 是空数组: 回调只会再函数组件调用执行一次(类似mounted)
- 是有元素的数组:元素为状态的话,状态更新,回调测重新执行一次
13. createContext作用
import './App.css';
import React, {useContext } from 'react'function App() {const AppContext = React.createContext()const A = () => {const {name} = useContext(AppContext)return (<p>A组件{name}嵌入了B<B/></p>)}const B = () => {const {name} = useContext(AppContext)return (<p>B组件{name}</p>)}return (<AppContext.Provider value={{name: 'context值'}}><A></A></AppContext.Provider>);
}export default App;
14. LayoutEffect Hook
useLayoutEffect() :和useEffect相同,都是用来执行副作用,但是它会在所有的DOM变更之后同步调用effect。useLayoutEffect和useEffect最大的区别就是一个是同步,一个是异步。从这个Hook的名字上也可以看出,它主要用来读取DOM布局并触发同步渲染,在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。官网建议还是尽可能的是使用标准的useEffec以避免阻塞视觉更新
15. 自定义组件
import { useState,useEffect } from "react";
const usePerson = ({name}) => {const [loading, setLoading] = useState(true)const [person, setPerson] = useState({})useEffect(() => {setLoading(true)setTimeout(()=> {setLoading(false)setPerson({name})},2000)},[name])return [loading,person]
}
const AsyncPage = (name)=> {const [loading,person] = usePerson(name)return (<>{loading?<p>Loading...</p>:<p>{ person.name }</p>}</>)
}const App = ()=> {const [state,setState] = useState('')const changeName = (name)=> {setState(name)}return (<><AsyncPage name={ state } /><button onClick={ ()=> { changeName('郭靖')}}>郭靖</button><button onClick={ ()=> { changeName('黄蓉')}}>黄蓉</button></>)
}export default App;
//上面代码中,封装成了自己的Hooks,便于共享。其中,usePerson()为自定义Hooks它接受一个字符串,返回一个数组,数组中包括两个数据的状态,之后在使用usePerson()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。//这是一种非常简单的自定义Hook。如果项目大的话使用自定义Hook会抽离可以抽离公共代码,极大的减少我们的代码量,提高开发效率。
16. 路由传参三种方式
react router v6 获取传参需要用两个 hook,分别是 useParams(params)和 useSearchParams(search)(1)useParamsparams 传参import { NavLink } from 'react-router-dom';{/* 路由定义 /article/:id */}
<NavLink to={`/article/1`}>文章1</NavLink>
接收参数import { useParams } from 'react-router-dom'/* params */
const params = useParams();
const { id } = params;(2)useSearchParamssearch 传参import { NavLink } from 'react-router-dom';<NavLink to={`/article?id=1`}>文章1</NavLink>
接收参数import { useSearchParams } from 'react-router-dom'/* search */
let [searchParams, setSearchParams] = useSearchParams();
const { id } = searchParams;
(3)useLocationstate 传参import { NavLink } from 'react-router-dom';<NavLink to="/article" state={{ id: 1 }}>文章1</NavLink>
接收参数import { useLocation } from 'react-router-dom'let location = useLocation();
const { id } = location.state;
17. 路由跳转
参考 [https://zhuanlan.zhihu.com/p/518339176]
useHistory 已废弃,而是使用 useNavigateimport { FC, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Button } from 'antd';interface IndexProps{}const Index: FC<IndexProps> = () => {const navigate = useNavigate();// 返回const handleBack = () => navigate(-1);// 前进const handleForward = () => navigate(1);// 刷新const handleRefresh = () => navigate(0);//return <><Button type="primary" onClick={handleBack}>返回</Button><Button type="primary" onClick={handleForward}>前进</Button><Button type="primary" onClick={handleRefresh}>刷新</Button>{/* 跳转路由 */}<Button type="primary" onClick={() => navigate('/article/1', { replace: true })}>params</Button><Button type="primary" onClick={() => navigate('/article?id=1', { replace: true })}>search</Button><Button type="primary" onClick={() => navigate('/article', { replace: true, state: { id: 1 } })}>state</Button></>
}export default Index;
18. 配置路径别名
//vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from 'path';
import {join} from 'node:path'
// https://vitejs.dev/config/
export default defineConfig({plugins: [react()],resolve: {alias: {'@': path.resolve(__dirname, './src/')// '@': join(__dirname, './src/')}}
})//tsconfig.json{"compilerOptions": {"baseUrl": ".","paths": {"@/*": ["./src/*"]},"target": "ES2020",......}