事件函数中的this问题
在react中事件的回调this是指向undefined。因此我们需要手动处理this问题,让this指向react组件实例。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);jsx
书写规范:
- 只能有一个根元素
- 为了方便阅读,我们通常在jsx的外层包裹一个小括号()
jsx中插入的类型分类:
- Number,String,Array会直接显示
- Null,undefined,Boolean 会显示为空白
- Object类型不能作为子元素直接插入{}中
属性绑定:
{/* 绑定age变量 */}
<div className={age}>{count}</div>
{/* 绑定style */}
<div className={age} style={{color:'red'}}>{count}</div>usestate更改状态
更改后的age为2.
import { useState } from "react";
export default function App () {
const [age,setage] = useState(0);
const click1 = () =>{
setage(age+1);
if(true){
setage(age+2);
}
}
const click2 = () =>{
setage(()=>age+1);
if(true){
setage(()=>age+2);
}
}
return (
<div>
<button onClick={click1}>one</button>
<button onClick={click2}>tow</button>
<span>{age}</span>
</div>
)
}setState的用法
setState改变状态是异步的。
问题:为什么setState设计为异步更新?
- 可以显著的提升性能。(获取多个更新后,进行批量更新)
- 如果同步更新了state,但是还没有执行render,会造成state和props不同步的问题。
传入对象时,由于是setState会将多了setState进行合并,所以setState对象中使用的原来的值都是0。
传入函数后,函数会被加入异步队列当中,会依次执行,那么两个异步函数中的state.age是不一样的。
//执行click1 后 age变为了2
//执行click2 后 age变为了3
import { Component } from "react";
interface State {
age: number;
}
export default class ClassComponents extends Component<{}, State> {
state: State = {
age: 0
}
click1 = () =>{
this.setState({
age:this.state.age+1
},()=>{
/* 该回调保证在界面已经根据最新的 state 完成更新后才被调用,
适合用来处理依赖最新 state 的 DOM 操作或副作用逻辑。 */
console.log(this.state.age); // 2
});
if(true){
this.setState({
age:this.state.age+2
});
}
}
click2 = () =>{
this.setState((state,props)=>({
//state.age =0
age: state.age+1
}));
if(true){
this.setState((state,props)=>(
//state.age =1
{age: state.age+2}
));
}
}
render (){
return (
<div>
<button onClick={this.click1}>one</button>
<button onClick={this.click2}>tow</button>
<span>{this.state.age}</span>
</div>
)
}
}由于setState是异步的,在改变state后,如果想等到state被修改之后再来使用,可以使用setState()的第二个参数callback().
add = ()=>{
this.setState({count:this.state.count+1},()=>{
// state完成合并后进行的回调
console.log(this.state.count);
})
console.log(this.state.count);
}生命周期

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
函数组件模拟生命周期方法:
只需要在 useEffect 的依赖数组中传入一个空数组 []。这样,该副作用只会在组件挂载后运行一次。
useEffect(() => {
console.log('代码只会在组件挂载后执行一次')
}, [])模拟更新阶段的生命周期方法:
通过将依赖项放入依赖数组中,useEffect 可以在依赖项更改时执行。如果你省略了依赖数组,副作用将在每次渲染后执行。
// 注意这里没有提供依赖数组
useEffect(() => {
console.log('代码会在组件挂载后以及每次更新后执行')
})
// 特定依赖更新时执行
useEffect(() => {
console.log('代码会在 count 更新后执行')
}, [count])模拟卸载阶段的生命周期方法:
在 useEffect 的函数中返回一个函数,该函数会在组件卸载前执行。
useEffect(() => {
return () => {
console.log('代码会在组件卸载前执行')
}
}, [])组件通信
父传子:props
使用prop-types来进行设置props的类型和默认值
import React from "react";
// 类组件
class Header extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
return <div>Header</div>;
}
}
// 函数组件
function Header(props) {
console.log(props);
return (
<>
<div>{props.age}</div>
<div>fewa</div>
</>
);
}子传父
相当于我们父组件给子组件传递一个函数props,然后我们在子组件中调用传入的函数,传入值后父组件进行接受。
父组件:
import React, { Component } from 'react'
import Header from './components/Header';
export class App extends Component {
state={
count:100
}
render() {
return (
<>
<div>{this.state.count}</div>
<Header add={(value)=>{this.setState({count:this.state.count + value})}}></Header>
</>
)
}
}子组件:
import React from "react";
function Header(props) {
console.log(props);
return (
<>
<button onClick={() => props.add(1)}>+1</button>
<button onClick={() => props.add(5)}>+5</button>
<button onClick={() => props.add(10)}>+10</button>
</>
);
}
export default Header;插槽的实现
1.通过children实现
- 传入多个子元素时,children是一个数组。
- 传入单个元素时,children就是传入的那个元素。
// 父组件
export class App extends Component {
state={
count:100
}
render() {
return (
<>
<div>{this.state.count}</div>
<Header>
<button>哈哈</button>
<span>嘿嘿嘿</span>
<button>feawfe</button>
</Header>
</>
)
}
}
// 子组件
function Header(props) {
console.log(props);
const {children} = props;
return (
<>
<div className="left">{children[0]}</div>
<div className="center">{children[1]}</div>
<div className="right">{children[2]}</div>
</>
);
}2.通过props传递元素实现
// 如组件直接掺入一个标签prop
<Header leftSlot = {<button>哈哈</button>}/>
// 子组件使用它
function Header(props) {
console.log(props);
return (
<>
<div className="left">{props.leftSlot}</div>
</>
);
}作用域插槽实现
// 父组件传入一个prop函数,可以通过参数接受到子组件中的值
render() {
return (
<>
<div>{this.state.count}</div>
<Header leftSlot = {(item)=><button>{item}</button>}/>
</>
)
}
// 子组件
function Header(props) {
console.log(props);
return (
<>
<div className="left">{props.leftSlot("自定义")}</div>
</>
);
}SCU优化
默认情况下,我们在调用setState之后,无论state中的值是否改变,都会执行render函数。如果值没有改变,那么这就会造成性能下降的问题。因此可以通过生命周期shouldComponentUpdate来判断当值发生改变以后再执行render函数。但是如果我们手动判断是一件非常麻烦的事情。react为我们提供了PureComponent 和memo来实现这样的性能优化。
PureComponent
定义:PureComponent 是 React 提供的一个基类组件。它的核心思想是:如果组件-的 props 和 state 没有发生“浅比较”(shallow comparison)的变化,则不会重新渲染组件。
特点:
- 继承自 Component:你需要让组件继承自 React.PureComponent 而不是 React.Component。
- 自动进行浅比较:React 内部会自动对 props 和 state 进行浅层比较。如果发现没有变化,就阻止组件的 render 方法执行。
- 适用于类组件:只能用于类组件(Class Components)。
- 浅比较限制:对于深层嵌套的对象或数组,浅比较可能无法正确检测到变化。例如,修改了对象内部的属性或数组内部的元素,浅比较可能认为对象没有改变。
- 手动控制:如果需要更复杂的比较逻辑,可以通过实现 shouldComponentUpdate 生命周期方法来覆盖默认的浅比较行为。
import React, { PureComponent } from 'react'
import Header from './components/Header';
// 继承pureComponent
export class App extends PureComponent {
state={
count:100
}
add = ()=>{
this.setState({count:this.state.count+1})
}
render() {
console.log('app.js');
return (
<>
<div>{this.state.count}</div>
<button onClick={this.add}>+1</button>
<Header></Header>
</>
)
}
}
export default App;memo
定义:memo 是 React 提供的一个高阶函数,用于对函数组件进行性能优化。它的核心思想是:如果组件的 props 没有发生“浅比较”(shallow comparison)的变化,则不会重新渲染组件。
特点:
- 应用于函数组件:memo 主要用于函数组件(Function Components)。
- 自动进行浅比较:和 PureComponent 类似,React 会自动对传入的 props 进行浅层比较。
- 可自定义比较函数:你可以传递第二个参数给 memo,它是一个比较函数,用来决定是否需要重新渲染。这个函数接收 prevProps 和 nextProps 作为参数,返回 true 表示不需要更新,返回 false 表示需要更新。
- 灵活性:相比 PureComponent,memo 更加灵活,因为它可以用于函数组件,并且可以自定义比较逻辑。
import React, {memo} from "react";
// 使用高阶函数memo,返回一个组件
const Header = memo(function (props) {
console.log("header.js");
return (
<>
<div>hahahah</div>
</>
);
});
export default Header;函数组件useRef绑定组件
使用 forwardRef 使子组件能接收 ref。(从 React 19 开始,你现在可以在函数组件中将 ref 作为 prop 进行访问)
在子组件内部使用 useImperativeHandle 来自定义通过 ref 暴露的内容。
// 子组件
import { useImperativeHandle, useRef,forwardRef } from 'react';
export interface FunctionComponentsHandle {
clickHandler: () => void;
h1:HTMLHeadingElement | null;
}
const FunctionComponents = forwardRef<FunctionComponentsHandle, {}>((props, ref) => {
console.log(ref,'ref');
const h1Ref = useRef<HTMLHeadingElement>(null);
function clickHandler() {
console.log("click");
}
useImperativeHandle(ref, () => ({
clickHandler, // 暴露函数
h1:h1Ref.current // 暴露dom
}));
return (
<div>
<h1 ref={h1Ref}>Function Components</h1>
</div>
);
});
export default FunctionComponents;// 父组件
import { useRef, useEffect } from 'react'
import FunctionComponents, { type FunctionComponentsHandle } from './components/functionComponents'
function App() {
const inputRef = useRef<HTMLInputElement>(null)
const functionRef = useRef<FunctionComponentsHandle>(null)
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus()
}
}
useEffect(()=>{
console.log(classRef.current)
},[])
return (
<>
<FunctionComponents ref={functionRef}/>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</>
)
}
export default App受控组件与非受控组件
1. 受控组件 (Controlled Components)
在受控组件中,表单数据由 React 组件的 state 管理。每当表单项发生变化时,都会触发 onChange 事件并更新 state,从而重新渲染组件。
特点:
- 数据驱动:状态是唯一的真理来源(Single Source of Truth)。
- 实时校验:可以在用户输入时立即进行校验或格式化。
示例代码:
import React, { useState } from 'react';
function ControlledInput() {
const [value, setValue] = useState('');
const handleChange = (e) => {
setValue(e.target.value);
console.log('当前输入值:', e.target.value);
};
return (
<div>
<label>受控组件: </label>
<input type="text" value={value} onChange={handleChange} />
<p>输入的内容: {value}</p>
</div>
);
}2. 非受控组件 (Uncontrolled Components)
在非受控组件中,表单数据由 DOM 节点本身处理。如果需要获取表单的值,通常使用 ref 来从 DOM 中抓取数据。
特点:
- 接近原生:更像传统的 HTML 表单处理方式。
- 简单直接:对于简单的表单或集成第三方非 React 库(如 jQuery 插件)时非常有用。
- 使用
defaultValue:设置初始值。
示例代码:
import React, { useRef } from 'react';
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
alert('提交的值: ' + inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<label>非受控组件: </label>
{/* 使用 defaultValue 设置初始值 */}
<input type="text" defaultValue="初始值" ref={inputRef} />
<button type="submit">提交</button>
</form>
);
}3. 受控 vs 非受控 总结对比
| 特性 | 受控组件 | 非受控组件 |
|---|---|---|
| 数据更新 | 必须通过事件处理器更新 state | 由 DOM 节点自动处理 |
| 数据源 | React 的 state | DOM 节点 |
| 实时获取值 | 支持(通过 state) | 不支持(需要手动读取 ref) |
| 初始值设置 | value={state} | defaultValue="xxx" |
| 适用场景 | 实时验证、动态过滤、强制格式化 | 简单表单、一次性取值、集成非 React 库 |
TIP
官方推荐:在大多数情况下,React 推荐使用 受控组件 来实现表单。
高阶组件
1.增强props
使用场景,通过高阶组件对context进行增强
app.jsx
import React, { PureComponent} from 'react'
import Header from './components/Header';
import ThemeContext from './context/ThemeContext';
export class App extends PureComponent {
state={
list:[],
}
render() {
console.log('app.js');
return (
<>
<ThemeContext.Provider value={{color:'red',size:'12px'}}>
<Header></Header>
</ThemeContext.Provider>
</>
)
}
}
export default App;withComponse.js 高阶组件
import ThemeContext from "./context/ThemeContext"
export function withComponse(WrapperCpm){
console.log(WrapperCpm);
return (props)=>{
return (
<ThemeContext.Consumer>
{(value)=>{
return (
<WrapperCpm {...value} {...props}></WrapperCpm>
)
}}
</ThemeContext.Consumer>
)
}
}Header组件
import React, {Component} from "react";
import {withComponse} from "../withComponse";
class Header extends Component {
render() {
console.log("header");
return <div>{this.props.color}-fewafeeee</div>;
}
}
// 调用withComponse函数进行context增强
export default withComponse(Header);2.拦截组件,验证登录
export function with_auth(WrapperCpm){
return (props)=>{
const auth = localStorage.getItem('auth')
if(auth){
return <WrapperCpm></WrapperCpm>
}else{
return <div>请登录!</div>
}
}
}3.组件渲染耗费的时间
import { Component } from "react";
export function withRenderTIme(WrapperCpm){
console.log(WrapperCpm);
return class extends Component{
state = {
startTime:0,
spendTime:0
}
componentWillMount(){
let newTime = new Date().getTime()
this.setState({startTime:newTime})
}
componentDidMount(){
let nowTime = new Date().getTime()
let spendTime = nowTime - this.state.startTime;
this.setState({spendTime})
}
render(){
return (
<>
<div>{WrapperCpm.name}渲染花费了{this.state.spendTime}毫秒</div>
<WrapperCpm></WrapperCpm>
</>
)
}
}
}编写css
1.css modules
将css文件命名为 【name】.module.css

import React, { useState } from 'react'
import homeStyle from './home.module.css';
class Home extends React.Component {
constructor(){
super()
this.state = {
name:'fewa'
}
}
render(){
return (
// 使用css module
<div className={homeStyle.title}>Home组件呀</div>
)
}
}2.css in js
使用styled-components来实现使用js来编写css
classNames添加类名
使用classNames库来添加类更方便。
