当前位置: 首页 > news >正文

React 进阶

  • 高阶组件HOC的用法与封装
    • 复用组件逻辑的一种高级技巧、设计模式
    • 接受组件为参数返回新组件(为纯函数,不改变传入的组件的前提下返回新组件)
    //登录高阶组件实现
    interface LoginProps {name: string
    }
    const Auth = (LoginCom:React.FC<LoginProps>):React.FC<LoginProps> =>{return (props) => {let isAuth = true; //模拟登录逻辑if(isAuth) {return <LoginCom {...props}></LoginCom>}else{return <div>您没有权限</div>}}
    }
    const LoginCom:React.FC<LoginProps> = (props:LoginProps) => {return <div>登录人{props.name}</div>
    }
    const AuthLoginCom = Auth(LoginCom)
    const App = () => {return <AuthLoginCom name='张三'></AuthLoginCom>
    }
    
    • 反向继承
      • 通过继承被包装组件来实现功能增强()
    // 原始组件
    class Message extends React.Component {render() {return <div>原始消息: {this.props.text}</div>;}
    }
    // 反向继承HOC
    function withEnhancement(WrappedComponent) {return class extends WrappedComponent {render() {const original = super.render();//通过super 劫持原始组件的 render return (<div style={{ border: '2px solid red', padding: '10px' }}><span>修改渲染</span>//修改渲染xxx{original}</div>);}};
    }
    // 使用
    const EnhancedMessage = withEnhancement(Message);
    function App() {return <EnhancedMessage text="Hello World" />;
    }
    
  • 状态抽离
    • 指的是将组件的状态与逻辑从组件中抽离出来,使得组件可复用的一种方式
    //状态切换高阶组件
    interface ToolProps {isOn: boolean,toolbtn: () => void
    }
    interface ToggleButtonProps  {label: string;
    }
    const WhthTool = <P extends ToggleButtonProps>(WrappedComponent:React.FC<P & ToolProps>):React.FC<P> => {return (props: P) => {// 传入WrappedComponent组件的状态以及逻辑在这里维护const [isOn, setOn] = useState(false);const toolbtn = () => {setOn((pre) => !pre)}return <WrappedComponent {...props} isOn={isOn} toolbtn={toolbtn}  />}
    }
    // 这里 ToolButton 组件的状态以及逻辑都抽离出去了
    const ToolButton:React.FC<ToggleButtonProps & ToolProps> = ({isOn = false, label, toolbtn}) => {return <button onClick={toolbtn}>{label}:{isOn?'开':'关'}</button>
    }
    const EnhancedToggleButton  = WhthTool (ToolButton)
    const App = () => {return (<div><EnhancedToggleButton label="电源" /><EnhancedToggleButton label="灯光" /></div>);
    }
    
  • 属性代理
    • 高阶组件接收一个组件,返回一个新组件,新组件负责管理props并传递给被包装组件
    interface ToolProps {isOn: boolean,
    }
    interface ToolAllProps extends ToolProps {color: string,name: string
    }
    const ToolButton:React.FC<ToolAllProps> = ({isOn = false, name, color}) => {return <button>{color + name}:{isOn?'开':'关'}</button>
    }
    const whthTool = <P extends ToolProps>(WrappedComponent:React.ComponentType<ToolAllProps>):React.FC<P> => {return (props) => {//新增propsconst newProps = {...props,color: '红',name: '电源'}return <WrappedComponent {...newProps}/>}
    }
    const EnhancedToggleButton  = whthTool(ToolButton)
    const App = () => {return (<div><EnhancedToggleButton isOn={true}/></div>);
    }
    

常用hooks

  • useState
    • 避免嵌套过深,因为useState 的更新逻辑是进行值得浅比较,使用的Object.is方法,对象嵌套过深值的变化 Object.is方法判断不出来
      • 无法避免的时候可以使用 immer库来解决
      import { useImmer } from 'use-immer';
      const [user, updateUser] = useImmer({name: '张三',age: 25,address: {city: '北京',district: '朝阳区'},hobbies: ['阅读', '音乐']});
      
    • 函数式更新
           setCount(prevCount => prevCount + 1);
      
    • 惰性初始化(只在初始渲染的时候才执行一次)
         const computeExpensiveValue = () => {//这里是庞大的计算}const [data, setData] = useState(() => {const expensiveValue = computeExpensiveValue();return expensiveValue;});
      
  • useEffect
    • 有依赖项 按依赖项的变动来触发 componetDidUpdate,如果没有依赖性相当于触发 componentDidMount;
    • 异步函数的使用 (避免直接使用异步函数例如:useEffect(async () => {}, [])
         useEffect(() => {async function fetch() {let res = await api()}fetch()},[])
      
    • 时间监听、定时器 以及 清理
        useEffect(() => {const handleResize = () => {console.log("窗口大小改变:", window.innerWidth);};const timer = setInterval(() => {console.log("定时器执行");}, 1000);window.addEventListener("resize", handleResize);return () => {window.removeEventListener("resize", handleResize);clearInterval(timer)};}, []);
      
    • useLayoutEffect
      • useLayoutEffect vs useEffect
        1. 执行时机不同: 执行useLayoutEffect清除函数 => 执行 useLayoutEffect副作用 => 浏览器绘制 => 执行useEffect清除函数 => 执行 useEffect副作用
        2. 上述流程可以看到useLayoutEffect执行在浏览器渲染之前所以会阻塞浏览器的绘制
      • 使用场景:读取dom布局、尺寸、位置;避免闪烁; 立即同步更新ui
    //聊天室滚动条定位const [messages, setMessages] = useState([{ id: 1, text: '你好!', sender: 'other' },{ id: 2, text: '你好,最近怎么样?', sender: 'me' },]);//聊天元素const chatContainerRef = useRef(null);// 关键:使用 useLayoutEffect 来控制滚动useLayoutEffect(() => {const chatContainer = chatContainerRef.current;if (chatContainer) {// 在浏览器绘制前同步滚动到底部chatContainer.scrollTop = chatContainer.scrollHeight;}}, [messages]); // 当消息列表变化时触发
    
  • useRef
    • 访问dom引用
      const App = () => {const inputRef = useRef<HTMLInputElement>(null)const handleBlur = () => {console.log(inputRef?.current?.value)}return (<input ref={inputRef} onBlur={handleBlur}  />);
      };
      
    • 存储定时器或事件监听器
       	const timer = useRef<number>(undefined);useEffect(() => {timer.current = setInterval(() => {console.log('定时器',timer)},1000)return () => clearInterval(timer?.current)});
      
    • 存储可变值,不触发渲染
       	const App = () => {const counter = useRef(0);const renderCount = useRef(0);useEffect(() => {renderCount.current += 1;});const increment = () => {counter.current += 1;console.log('当前值:', counter.current); // 不会触发重渲染};return (<div><p>组件渲染次数: {renderCount.current}</p><p>计数器值: {counter.current}</p><button onClick={increment}>增加</button></div>);};
      
    • useRef 对比 useState
      • useState会的setter函数会触发重新渲染, useRef 不会
      • useState 为异步批量更新, useRef 为同步更新
      • useState 初始值每次渲染可能重新赋值,useRef初始值只在挂载时设置一次
  • useMemo
    • 缓存的是计算后的值(根据依赖渲染期间计算,计算的值参与渲染)
    • 高开销计算、避免重复渲染
    • 避免重新创建引用
    • 依赖项目笔画频率低
    interface PersonProps {name: string,age: number
    }
    interface ChildProps{label: string
    }
    // 子组件
    const Child:React.FC<ChildProps> = (props) => {console.log('触发子组件渲染') //这行只会在挂载阶段执行一次return <div>我是{props.label}</div>
    }
    const App = () => {const [info, setInfo] = useState<PersonProps>({name:'张三',age: 14})const [label, setLabel] = useState<string>('子组件')const handleClick = ():void => {setInfo({name: '王五', age:20})}//这里使用useMemo包裹Child 组件,当App 重新渲染的时候不会触发Child组件的重新渲染const newChild = useMemo(() => <Child label= {label}></Child>, [])return (<div><span>姓名:{info.name}, 年龄:{info.age}</span><button onClick={handleClick}>换人</button>{newChild}</div>)
    }
    
    • 创建稳定的引用
    const obj = { theme: 'dark',size: 'large'} //这种方式重新渲染的时候会重新创建
    const config = useMemo(() => ({theme: 'dark',size: 'large'
    }), []); // 空依赖数组,只创建一次
    
  • useCallback
    • 缓存函数引用;避免重复创建函数 避免子组件不必要的渲染;
    • 完整的依赖列表 避免闭包问题;
    • 空依赖项 纯工具函数;
      const Component = () => {const [count, setCount] = useState<number>(0);const [name, setName] = useState<string>("张三");const handleClick_F = useCallback((): void => {console.log("第一个按钮", count, name);}, [count, name]); // 依赖 count 和 name 重新创建引用const handleClick_S = useCallback((): void => { //重新渲染不会重新创建引用console.log("第二个按钮");}, []);return (<div><button onClick={handleClick_F}>第一个按钮</button><button onClick={handleClick_S}>第二个按钮</button></div>);};
    
  • useReducer 、useContext 为状态管理相关,会单独出一期React的状态管理
  • 自定义hooks
    • 名称必须以 use 开头
    • 可以调用其他 Hook
    • 只能在 React 函数组件或自定义 Hook 中调用
    //开关hooks
    const useToogle = (init:boolean) => {const [value, setValue] = useState<boolean>(init);const toogleBtn = useCallback(() =>  setValue(prev => !prev),[])return [value, toogleBtn]
    }
    const App = () => {const [isOn, toogleBtn] = useToogle(false)return (<div><button onClick={toogleBtn}>当前灯:{isOn?'开': '关'}</button></div>);
    };
    

文章转载自:

http://z6bQtNCd.pzcqz.cn
http://R8IDyHOx.pzcqz.cn
http://ouin1m21.pzcqz.cn
http://0jjAbD3r.pzcqz.cn
http://tVn9TSqo.pzcqz.cn
http://R65j8oFl.pzcqz.cn
http://u1vPjsKh.pzcqz.cn
http://axb1UuYE.pzcqz.cn
http://MP8A86Di.pzcqz.cn
http://A7xZqNaA.pzcqz.cn
http://c58vMy9d.pzcqz.cn
http://cdcZpDR8.pzcqz.cn
http://MRly7xdM.pzcqz.cn
http://WqEGOvuY.pzcqz.cn
http://49tvsfWo.pzcqz.cn
http://AX6Pc21b.pzcqz.cn
http://wd3JuMa0.pzcqz.cn
http://IlO3tRuB.pzcqz.cn
http://VLi4jrlx.pzcqz.cn
http://L8zPBBhf.pzcqz.cn
http://nWnI1Glb.pzcqz.cn
http://JGUWkhtT.pzcqz.cn
http://jBYYPAQE.pzcqz.cn
http://lxS1jJUF.pzcqz.cn
http://k2ETT18I.pzcqz.cn
http://1KG208ea.pzcqz.cn
http://lYANy1qt.pzcqz.cn
http://Gs6vx3Al.pzcqz.cn
http://Hgl3HA6A.pzcqz.cn
http://fQzxeXpr.pzcqz.cn
http://www.dtcms.com/a/379831.html

相关文章:

  • ES相关问题汇总
  • 为什么Cesium不使用vue或者react,而是 保留 Knockout
  • Mysql杂志(十五)——公用表达式CTE
  • Javascript忘记了,好像又想起来了一点?
  • AI + 制造:NebulaAI 场景实践来了!
  • mosdns缓存dns服务器配置记录
  • android14 硬键盘ESC改BACK按键返回无效问题
  • 代码随想录算法训练营第62天 | Floyd 算法精讲、A * 算法精讲 (A star算法)、最短路算法总结篇、图论总结
  • 教程:用免费 Google Translate API 在 VSCode 中实现中文注释自动翻译英文
  • 数据储存方式
  • Java生态圈核心组件深度解析:Spring技术栈与分布式系统实战
  • 解决Ubuntu中apt-get -y安装时弹出交互提示的问题
  • 硅基计划3.0 Map类Set类
  • Ubuntu20.04手动安装中文输入法
  • 算法训练营DAY60 第十一章:图论part11
  • java 反射Class类/加载类/创建对象及方法
  • RL【9】:Policy Gradient
  • Java短链接生成服务实战指南
  • JAVA Web —— A / 网页开发基础
  • TensorFlow深度学习实战:从零开始构建你的第一个神经网络
  • Keepalived 负载均衡
  • 智能文档处理业务,应该选择大模型还是OCR专用小模型?
  • 《Redis核心机制解析》
  • Netty 在 API 网关中的应用篇(请求转发、限流、路由、负载均衡)
  • 金蝶云星空插件开发记录(一)
  • Knockout-ES5 入门教程
  • 基于 Art_DAQ、InfluxDB 和 PyQt 的传感器数据采集、存储与可视化
  • 【图像处理基石】图像压缩有哪些经典算法?
  • C语言实战:简单易懂通讯录
  • youte-agent部署(windows)