React 基础
对比学习react, 以vue 为例
Vue3 的双向绑定: state.xxx = xxx => Proxy set => trigger(触发更新) => effect (执行副作用函数) => 更新dom
React(类组件):onClick => dispatch => enqueueSetState(更新队列) => scheduleUpdateOnFiber(标记fiber节点,启动更新)
学习的时候尽量结合ts一起
-
类组件与函数组件(函数组件的开发效率更高,用的也更多一些)
-
类组件:
interface AppState {name: string,value: string}class App extends React.Component<AppState> {state:AppState = {name:'react',value: '类组件'};render():React.ReactNode {return <div>{`我是${this.state.name + this.state.value}`}</div>}}```
-
函数组件:
interface AppState {name: string,value: string}const App = () => {const [myState, setState] = useState<AppState>({name: 'react', value : '函数组件'})return (<div className="content"><h1>Rsbuild with React</h1><p>{`我是${myState.name + myState.value}`}</p></div>);};
-
受控组件与非受控组件(以表单元素举例,自定义组件也适用)
- 受控组件(指表单元素的值由React组件的state完全控制的组件)
const App= () => {const [name, setName] = useState<string>('')const handleChange = ():void => {setName('张三')}return (<div className="content"><input value={name} onClick={handleChange}></input></div>); };
- 非受控组件(表单元素的值由DOM自身管理,而不是由React state控制的组件)
const App= () => {const inputRef = useRef<HTMLInputElement>(null);const handleChange = ():void => {console.log(inputRef.current?.value)}return <div><input ref={inputRef}></input><button onClick={handleChange}>获取input值</button></div> };
- 受控组件(指表单元素的值由React组件的state完全控制的组件)
-
props 与 state
interface PersonState {name: string,age: number, } interface PersonComProps {person: PersonState } //props 外部传入使用 const PersonCom:React.FC<PersonComProps> = (props) => {return <div><span>姓名: {props.person.name}</span><span>年龄:{props.person.age}</span></div> } const App= () => {//通过useState hooks 声明stateconst [person, setPerson] = useState<PersonState>({name: '张三',age: 18})const changePerson = ():void => {setPerson({name: '王五',age: 20})}return <div><PersonCom person ={person} /><button onClick={changePerson}>切换人物</button></div> };
-
条件渲染与列表(React 非常灵活,编写jsx的时候可以就当作我们在写js)
interface User {name: string}const App = () => {const [show, setShow] = useState<boolean>(true);const [list] = useState<User[]>([{name:'张三'},{name:'李四'},{name:'王五'}])const handleChange = (): void => {setShow(!show);};return (<div>{/* 三元判断渲染 */}<div>{show ? "展示这个" : "展示另一个"}</div><button onClick={handleChange}>切换展示</button>{/* list循环渲染 */}{list.map((item,index) => <div key={index}>{item.name}</div>)}</div>);};
-
React 生命周期(这里描述的是19版本的类组件; 函数组件大多数使用useEffect、useMemo等hooks模拟生命周期操作)
- 挂载阶段
constructor
(初始化数据)- 调用 super(props) 继承React组件
- 初始化组件的state状态
- 为方法绑定this上下文(解决this指向问题)
- 创建ref引用等准备工作
static getDerivedStateFromProps
(在渲染前根据新的props来更新state)- 本质上是一个静态函数 传入state 和 props ,返回值会和当前的state 合并
render
(渲染)- 返回JSX描述UI结构
- 必须是纯函数,不能修改state或与DOM交互
- 准备虚拟DOM,为后续的DOM更新做准备
componentDidMount
(组件挂载完成, 这里一般可以请求接口)
- 更新阶段
static getDerivedStateFromProps
shouldComponentUpdate
(是否应该更新)- 比较新旧props和state
- 返回true允许更新,返回false阻止更新
- 性能优化操作
render
getSnapshotBeforeUpdate
(获取更新前快照,React更新过程中会通过计算虚拟dom去更新真实dom,这使得我们会丢失一些想保存的状态,例如聊天室中当前的滚动位置,可以通过此声明周期将位置信息传给 componentDidUpdate)// 在聊天室中,新消息到来时保持当前滚动位置 getSnapshotBeforeUpdate(prevProps, prevState) {if (prevProps.messages.length < this.props.messages.length) {// 捕获滚动容器的滚动高度return this.chatContainer.scrollHeight - this.chatContainer.scrollTop;}return null; }componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot !== null) {// 根据之前计算的滚动位置调整UIthis.chatContainer.scrollTop = this.chatContainer.scrollHeight - snapshot;} }
componentDidUpdate
(更新完成)
- 卸载阶段
componentWillUnmount
(组件将要卸载)- 事件监听函数/定时器的销毁操作
static getDerivedStateFromError
(捕获子组件的渲染错误)componentDidCatch
(捕获子组件的js错误)- 异常兜底操作,防止白屏
- 挂载阶段
-
React 事件相关
- 阻止默认事件:
e.preventDefault();
- 阻止事件传播:
stopPropagation();
- dom绑定事件
const handleListen = (): void => {console.log('focus了')};useEffect(() => {inputRef.current?.addEventListener("focus", handleListen);return () => {inputRef?.current?.removeEventListener("focus", handleListen);};}, []);
- 自定义DOM事件
- 使用
new CustomEvent
来创建自定义事件 - 使用
document.dispatchEvent
触发自定义事件
- 使用
const App = () => {//自定义事件hooksconst useCustomEvent = (eventName: string, detail: any) => {const dispathEvent = useCallback((customDetail?: any) => {const event = new CustomEvent(eventName, {detail: customDetail || detail,});document.dispatchEvent(event);},[eventName, detail]);return dispathEvent;};const dispathMyEvent = useCustomEvent("myEvent", "hello word");const handleMyEvent = (): void => {console.log("触发了自定义事件");};useEffect(() => {//监听事件document.addEventListener("myEvent", handleMyEvent);return () => {document.removeEventListener("myEvent", handleMyEvent);};}, []);return (<div>{/* //触发自定义事件 */}<button onClick={() => dispathMyEvent()}>触发自定义事件</button></div>); };
- 阻止默认事件: