赞
踩
如何让页面动起来(实时更新)
import React,{FC,useState} from "react"; const Demo:FC=()=>{ let count=0; //普通js变量无法触发组件更新 function add(){ count++; console.log("count: ",count); } return <div> <button onClick={add}>add {count}</button> </div> } export default Demo;
state的改变可以触发函数组件的更新
(如果js变量不在jsx中使用,别用useState)
import React,{FC,useState} from "react"; const Demo:FC=()=>{ // let count=0; //普通js变量无法触发组件更新 const[count,setCount]=useState(0); //useState 可以触发组件更新 function add(){ // count++; setCount(count+1) console.log("count: ",count); } return <div> <button onClick={add}>add {count}</button> </div> } export default Demo;
不可变数据
不修改原数据,而是传入新的值
异步更新|
函数中无法直接获取最新的值
可能被合并
使用函数state可以解决这个问题
setCount(()=>{
return count+5
})
是一个插件
解决state不可变数据的影响
安装 npm install -D immer
import {FC,useState} from "react"; import {produce} from "immer"; const Demo : FC =()=>{ const [list,setList]=useState(['x','y']) function add(){ // setList(list.concat('z')); setList( produce(draft=>{ draft.push('z')//直接在原数组上修改 }) ) } return ( <div> <h2>state 不可变数据</h2> <div>{JSON.stringify(list)}</div> <button onClick={add}>add item</button> </div> ) } export default Demo;
组件是一个函数,返回的是JSX片段,
组件初次渲染完成,即函数执行完成,
一般情况下函数执行完成就结束了
但是在state更新时,会触发组件更新,函数组件再次执行
为了解决以上问题,使用useEffect
import {FC,useEffect,useState} from "react" const Demo:FC=()=>{ useEffect(()=>{ console.log("组件初次渲染完成"); return ()=>{ console.log("组件销毁时执行"); } },[])//无依赖项,只执行一次 const[count,setCount]=useState(0); //useState 可以触发组件更新 function add(){ // count++; setCount(()=>{ return count+5 }) console.log("count: ",count); } return <div> <button onClick={add}>add {count}</button> </div> } export default Demo;
发现useEffect 执行两次
在react 18 中,useEffect 在开发环境中执行两次,在生产环境中执行一次
(模拟组件生命周期,以便尽早暴露问题)
开发环境 npm run build 生存环境
与vue3 ref 不同
import { FC,useRef } from "react"; const Demo:FC = () => { // 定义一个ref const inputRef = useRef<HTMLInputElement>(null) //dom节点 const nameRef=useRef("pink") //不是dom节点,是一个普通的js变量 function selectInput(){ const input = inputRef.current; if(input){ input.select(); // 选中输入框中的内容(Dom节点操作API) } } function changeName(){ nameRef.current="blue"//修改ref的值,不引起rerender(state修改会触发组件更新) console.log(nameRef.current); } return ( <> <input ref={inputRef} defaultValue={"hello world"}/> <button onClick={selectInput}>选中 input</button> <p>{nameRef.current}</p> <button onClick={changeName}>修改名字</button> </> ) } export default Demo;
缓存数据
不用每次执行函数组件(比如说 state修改)都重新生成 , 实现性能优化
import {FC,useMemo,useState} from "react" const Demo : FC =()=>{ const[num1,setNum1]=useState(0) const[num2,setNum2]=useState(0) const[text,setText]=useState("hello") //更新导致组件rerender const sum =useMemo(()=>{ console.log("计算两数之和");//缓存 return num1+num2; },[num1,num2]) return ( <> <p>sum = {sum}</p> <button onClick={()=>{setNum1(num1+1)}}>num1 : {num1}</button> <button onClick={()=>{setNum2(num2+1)}}>num2 : {num2}</button> <button onClick={()=>{setText(text+"1")}}>text : {text}</button> </> ) } export default Demo;
缓存函数
import {FC,useCallback,useState} from "react" const Demo : FC =()=>{ const [text,setText] = useState("hello") const fn1=()=>{ console.log("fn1 text",text); } const fn2=useCallback(()=>{ console.log("fn2 text",text); },[text]) return( <> <button onClick={fn1}>fn1</button> <button onClick={fn2}>fn2</button> <div> <input value={text} onChange={e=>setText(e.target.value)}></input> </div> </> ) } export default Demo;
抽离公共部分,复用代码
import { useState,useEffect } from "react"; //获取鼠标位置 function useMouse(){ const [x,setX]=useState(0); const [y,setY]=useState(0); const mouseMoveHandler=(e:MouseEvent)=>{ setX(e.clientX); setY(e.clientY); } useEffect(()=>{ //监听鼠标事件 window.addEventListener('mousemove',mouseMoveHandler); //组件销毁时,一定要解绑DOM事件!!(可能会导致内存泄漏问题) return ()=>{ window.removeEventListener('mousemove',mouseMoveHandler); } },[]) return {x,y} } export default useMouse;
//导入自定义hook import useMouse from "./hooks/useMouse.ts"; function App() { const {x, y} = useMouse(); return( <> <p> App Page </p> <div>x: {x}</div> <div>y: {y}</div> </> ) } export default App
import { useState,useEffect } from "react"; //异步获取消息 function getInfo():Promise<string>{ return new Promise(resolve=>{ setTimeout(()=>{ resolve(Date.now().toString()) },1500) }) } //自定义钩子 const useGetInfo=()=>{ const[loading,setLoading]=useState(true) const[info,setInfo]=useState("") useEffect(()=>{ getInfo().then(info=>{ setLoading(false) setInfo(info) }) },[]) return {loading,info} } export default useGetInfo;
提高开发效率
无法获取最新值
import {FC,useState} from 'react' const Demo:FC =()=>{ const[count,setCount]=useState(0) function add(){ setCount(count+1) } function alertFn(){ setTimeout(()=>{ alert(count); },3000) } return( <> <p>闭包陷阱</p> <div> <span>{count}</span> <button onClick={add}>add</button> <button onClick={alertFn}>alert</button> </div> </> ) } export default Demo;
使用ref解决
import {FC,useState,useRef, useEffect} from 'react' const Demo:FC =()=>{ const[count,setCount]=useState(0) const countRef = useRef(0) function add(){ setCount(count+1) } useEffect(()=>{ countRef.current=count },[count]) function alertFn(){ setTimeout(()=>{ // alert(count); //count 值类型 alert(countRef.current); // ref 引用类型 },3000) } return( <> <p>闭包陷阱</p> <div> <span>{count}</span> <button onClick={add}>add</button> <button onClick={alertFn}>alert</button> </div> </> ) } export default Demo;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。