React核心语法
jsx一二事
- 只能返回单个根元素
- 可以进行插值
{}
- 条件渲染
- 列表渲染
Fragment等价于<></>
只能有一个根元素,每个节点又只能有一个唯一的key,而且空标签不能设置key值,这时就要用到Fragment
- 函数式组件的状态管理
useState()
对于对象状态,修改其中某一个属性
setData({
...data,
title:’新标题’
})
对于数组状态
- 添加
用... - 删除
用filter
DOM组件
https://zhuanlan.zhihu.com/p/30659051
React支持的所有HTML和SVG标签可以书写一个变量来设置style
组件间通信
- props
父组件通过props向子组件传值,props都是只读的
- 子组件向父组件传值
父组件向子组件传递一个回调函数,子组件通过调用这个回调函数进行父组件需要的操作 - context
用useContext传递level实现标题逐级递减
import './App.css';
import { Children, createContext, useContext } from 'react';
const LevelContext=createContext(0)
function Heading({children}){
const level=useContext(LevelContext)
switch(level){
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
default:
throw Error('未知的Level:'+level)
}
}
function Section({children}){
const level=useContext(LevelContext)
return (
<section className='section'>
<LevelContext.Provider value={level+1}>
{children}
</LevelContext.Provider>
</section>
)
}
function App() {
return (
<div>
<Section>
<Heading>h1</Heading>
<Section>
<Heading>h2</Heading>
<Heading>h2</Heading>
<Section>
<Heading>h3</Heading>
</Section>
<Heading>h2</Heading>
</Section>
<Section>
<Heading>h2</Heading>
<Heading>h2</Heading>
</Section>
</Section>
</div>
);
}
export default App;
常用的Hooks
Reducer
进行统一状态管理
import { useReducer } from 'react';
import './App.css';
function conutReducer(state,action){
switch(action.type){
case "increment":
return state+1
case "decrement":
return state-1
default:
throw new Error()
}
}
function App() {
const [state,dispatch]=useReducer(conutReducer,0)
const hadleIncrement=()=>dispatch({type:"increment"})
const hadleDecrement=()=>dispatch({type:"decrement"})
return (
<div >
<button onClick={hadleIncrement}>increment</button>
<span>{state}</span>
<button onClick={hadleDecrement}>decrement</button>
</div>
);
}
useRef
使得父组件能够使用子组件中的方法
import { forwardRef, Fragment, useImperativeHandle, useRef } from 'react';
import './App.css';
const Child=forwardRef(function(props,ref){
useImperativeHandle(ref,()=>({
myFn:()=>{
console.log("子组件myfn")
}
}))
return (
<div>子组件</div>
)
})
function App() {
const inputRef=useRef()
const childRef=useRef()
function handleClick(){
inputRef.current.focus()//光标选中效果
childRef.current.myFn()
}
return (
<Fragment>
<div>
<input type="text" ref={inputRef}></input>
<button onClick={handleClick}>click here</button>
</div>
<Child ref={childRef}/>
</Fragment>
);
}
export default App;
useEffect
https://www.cnblogs.com/meme-/p/18813537
useMemo
使得父组件需要重新渲染时子组件内的复杂操作不会重新执行
function DoSomeMath({value}){
const result=useMemo(()=>{
console.log("子组件重新执行")
let result=0
result=value*2
return result
},[value])//和useEffect一样的依赖数组
return (
<div>
<span>输入{value}</span>
<span>输出{result}</span>
</div>
)
}
useCallback
使得父组件重新渲染时子组件不会重新渲染
- 用React的memo方法把子组件定义为记忆组件,只要传入子组件的prop不变,子组件就不会重新渲染
- 父组件重新渲染时,父组件中的函数引用会发生变化,所以要用useCallback来定义传给子组件的prop
但是有内存泄露的风险,详见闭包
const Button =memo(function({onClick}){
return <Button onClick={onClick}>子组件的Button</Button>
})
const handleClickButton=useCallback(()=>{
console.log("")
},[])
抛开组件间通信谈一谈这些hook的使用场景
useEffect
处理副作用时
当你需要再组件渲染完成后执行某些操作时使用。
- 常见场景
- 数据获取:组件挂载后从API获取数据
- 设置订阅:如事件监听器,WebSocket连接
- 需要在组件卸载时进行清理的操作
useMemo
缓存计算结果
- 常见场景
- 对大型数组中进行复杂转换或过滤
- 需要昂贵计算才能得到的派生状态
- 返回一个记忆化的值
- 只有依赖项数组中的某个依赖发生变化时才会重新计算,如果依赖项没有改变,会返回上一次缓存的值
useCallback
缓存函数本身
将一个回调函数传递给子组件并且希望避免因为父组件重新渲染导致这个回调函数被重新创建,从而避免子组件不必要的重新渲染。
- 常见场景
- 将事件函数传递给React.memo优化的子组件
由于React.memo使用的是浅比较,依赖引用相等性所以当父组件重新渲染时会给这个事件函数生成一个新的引用,这时子组件就会认为该回调函数发生了变化,于是重新渲染子组件。
所以可以用useCallback包装事件函数,当只有依赖项发生变化时才创建一个新的函数实例
- 当回调函数是useEffect或其他Hook的依赖项时用useCallback包装
如果一个函数是 useEffect 或 useMemo 的依赖项,并且这个函数在每次渲染时都是一个新的实例(即引用变化),那么这可能会导致 useEffect 或 useMemo 在每次渲染后都不必要地重新执行。