React学习笔记

React基础

搭建环境

使用脚手架创建一个react项目 (要有node环境,node环境搭建点击)

  • 打开powershell
  • 执行命令
npx create-react-app 项目名
// npx 命令会帮助我们临时安装create-react-app包,然后初始化项目完成之后会自自动删掉,所以不需要全局安装create-react-app
  • 项目启动
npm start

JSX基础

概念:JSX是 JavaScript XML(HTML)的缩写,表示在 JS 代码中书写 HTML 结构

作用:在React中创建HTML结构(页面UI结构)

优势:

  1. 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
  2. 充分利用JS自身的可编程能力创建HTML结构
JSX中使用js表达式

语法

{js表达式}

const name = 'jsl'
const getAge = () => {
    return 18
}

function App() {
    return (
        <div className="App">
            {name}
            {getAge()}
            {1 < 9 ? 10 : 9}
        </div>
    );
}

export default App;

{}中的表达式可以是:字符串、数值、布尔值、null、undefined、object( [] / {} )

1 + 2、'abc'.split('')、['a', 'b'].join('-')、function

if 语句/ switch-case 语句/ 变量声明语句,这s是做语句,不是表达式,不能出现在{}

JSX列表渲染

使用数组的map方法实现

// 来个列表
const songs = [
    { id: 1, name: '痴心绝对' },
    { id: 2, name: '像我这样的人' },
    { id: 3, name: '南山南' }
]
// 渲染列表:使用map重复渲染的是哪个模板,就return 出去
// 遍历列表同时需要提供一个类型为number/string不可重复的key值,提高diff性能
// key仅在内部使用,不会出现在真是的DOM中,不提供key属性会有警告

function App() {
    return (
        <div className="App">
            <ul>
                {songs.map(song => <li key={song.id}>{song.name}</li>)}
            </ul>
            {<span>hello</span>}
        </div>
    );
}

export default App;
JSX条件渲染

可以使用 三元运算符逻辑与(&&)运算符

// 条件渲染
// 技术方案:三元表达式(常用)  逻辑&&运算
// 复杂的分支逻辑的话可以收敛为一个函数
const getHTag = (type) => {
    if (type === 1)
        return <h1>hello</h1>
    if (type === 2)
        return <h2>hello</h2>
    if (type === 3)
        return <h3>hello</h3>
}

const flag = true
const name = 'jsl'
function App() {
    return (
        <div className="App">
            {flag ? <span>hello</span> : null}

            {flag && name}

            {getHTag(1)}
            {getHTag(2)}
        </div>
    );
}

export default App;
JSX样式处理
// jsx 样式控制
// 行内样式 - 在元素身上绑定一个style属性
// 类名样式 - 元素身上绑定一个className属性
import './app.css'
const style = {
    color: 'red',
    fontSize: '30px'
}

let show = true

function App () {
    return (
        <div className="App">
            {/* 行内样式 - style  */}
            <span style={{ color: 'red', fontSize: '30px' }}>this is span</span>
            <br />
            {/* 行内样式 - style - 更优写法  */}
            <span style={style}>行内样式</span>
            {/* 类名 - className - 动态类名控制  */}
            <span className={show ? 'active' : ''}>类名样式</span>
        </div>
    )
}

export default App
JSX 注意事项
  1. JSX必须有一个根节点,如果没有根节点,可以使用<></>(幽灵节点)替代
  2. 所有标签必须形成闭合,成对闭合或者自闭合都可以
  3. JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class -> className for -> htmlFor
  4. JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现

格式化配置参考

React组件基础

组件定义
import './app.css'
import React from 'react'

// 函数组件的创建和渲染
function Hello () {
    return <h2>hello</h2>
}
// 类组件的创建和渲染
class HelloComponent extends React.Component {
    render () {
        return <div>this is a class component</div>
    }
}

function App () {
    return (
        <div className="App">
            <Hello />
            <Hello></Hello>
            <HelloComponent></HelloComponent>
        </div>
    )
}

export default App

函数组件说明:

  1. 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
  2. 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
  3. 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
  4. 使用函数名称作为组件标签名称,可以成对出现也可以自闭合

类组件说明:

  1. 类名称也必须以大写字母开头
  2. 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
  3. 类组件必须提供 render 方法render 方法必须有返回值,表示该组件的 UI 结构
事件绑定
import './app.css'
import React from 'react'
// 绑定事件:语法:on+事件名称={事件处理程序} 例如:<div onClick={()=>{}}></div>
// react事件采用驼峰命名法,比如:onMouseEnter、onFocus
// 函数组件的创建和渲染
function Hello () {
    const clickHello = () => {
        console.log('点击事件')
    }
    return <h2 onClick={clickHello}>hello</h2>
}
// 类组件的创建和渲染
class HelloComponent extends React.Component {
    // 事件回调函数(标准写法)
    // 这样写,函数中的this指向当前组件的实例对象
    clickHandler = () => {
        console.log('类点击事件')
    }
    render () {
        return <div onClick={this.clickHandler}>this is a class component</div>
    }
}

function App () {
    return (
        <div className="App">
            <Hello />
            <Hello></Hello>
            <HelloComponent></HelloComponent>
        </div>
    )
}

export default App
阻止默认事件

获取事件对象e只需要在 事件的回调函数中 补充一个形参e即可拿到

function Hello () {
    const clickHello = (e) => {
        // 阻止默认行为
        e.preventDefault()
        console.log('点击事件', e)
    }
    return (<div>
        <a onClick={clickHello} href="https://www.baidu.com">百度</a>
    </div>)
}


function App () {
    return (
        <div className="App">
            {/* <Hello /> */}
            <Hello></Hello>
        </div>
    )
}

export default App
传递自定义参数

改造事件绑定为箭头函数 在箭头函数中完成参数的传递

// 传递自定义参数
// 1. 只需要一个额外参数 {() => clickHandler('自定义参数')}
// 2. 既需要e也需要自定义参数 {(e) => clickHandler(e, '自定义参数')}
function Hello () {
    const clickHello = (e, msg) => {
        e.preventDefault()
        console.log('点击事件', msg)
        console.log(e)
    }
    return (<div onClick={(e) => clickHello(e, 'this is msg')}>hello</div>)
}


function App () {
    return (
        <div className="App">
            <Hello></Hello>
        </div>
    )
}

export default App
组件状态

在React hook出来之前,函数式组件是没有自己的状态的,以类组件为例

在这里插入图片描述

看代码 -> 看注释

// 组件状态
import React from "react"

class TestComponent extends React.Component {
    // 定义这个类组件的状态
    state = {
        // 可以定义属于这个组件的属性
        name: 'cp teacher'
    }


    clickHandler = () => {
        // 需要使用setState({})进行状态属性的修改 -> 更新UI -> 
        // 数据驱动视图,也就是只要修改数据状态,那么页面就会自动刷新,无需手动操作dom 
        // React 状态不可变,不要直接修改状态的值,而是基于当前状态创建新的状态值
        this.setState({
            name: 'jsl'
        })
    }

    render () {
        return (
            <>
                <div>{this.state.name}</div>
                <button onClick={this.clickHandler}>点击</button>
            </>

        )
    }
}
/**
 * 总结:
 * 1. 编写组件其实就是编写原生的js类或者函数
 * 2. 定义状态必须通过state实例属性的方法,提供一个对象,名称是固定的
 * 3. 修改state中的任何属性,都不可以通过直接赋值,必须走setState方法,这个方法来自于继承得到
 * 4. 这里的this关键词,很容易出现指向错误问题,上面的写法是最规范的也是最标准的,没有this指向问题
 * 
 */


function App () {
    return (
        <div>
            <TestComponent></TestComponent>
        </div>
    )
}

export default App
改变this指向问题

直接看代码看注释

import React from "react"

// 非规范写法,需要改变this指向问题
class Counter extends React.Component {

    add () {
        console.log(this)
        // 不改变this的指向去使用this.setState去修改数据就会报错了 
    }

    render () {
        // render函数中的this已经被react内部修正过了
        // 这里的this指向就是当前的实例对象
        // 那我们箭头函数中的this直接沿用,指向的也是当前实例对象
        console.log('父级函数this的指向:', this)
        return (
            <>
                {/* 如果输出的this是undefined,可以在初始化的时候修改this指向,或者在执行的时候通过bind改变this指向 */}
                <button onClick={this.add.bind(this)}>click</button>
                {/* 通过箭头函数的写法,直接沿用父函数中的this也是OK的 */}
                <button onClick={() => this.add()}>click</button>
            </>
        )
    }
}

function App () {
    return (
        <div>
            <Counter></Counter>
        </div>
    )
}

export default App
受控表单组件

受控组件 (推荐使用)

React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,Reactstate与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性

  1. 在组件的state中声明一个组件的状态数据
  2. 将状态数据设置为input标签元素的value属性的值
  3. 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(即用户当前输入的值
  4. 调用setState方法,将文本框的值作为state状态的最新值
import React from "react"

class InputComponent extends React.Component {
    state = {
        inputValue: 'this is message'
    }

    inputChange = (e) => {
        this.setState({
            inputValue: e.target.value
        })
        console.log('onChange事件触发了')
    }

    render () {
        return (
            <div>
                <input type="text" value={this.state.inputValue} onChange={this.inputChange} />
            </div>
        )
    }
}

function App () {
    return (
        <div>
            <InputComponent></InputComponent>
        </div>
    )
}

export default App
非受控表单组件

非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值

  1. 导入createRef 函数
  2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中
  3. 为input添加ref属性,值为msgRef
  4. 在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值
import React, { createRef } from "react"

class InputComponent extends React.Component {
    // 创建一个DOM容器
    inputMsg = createRef()

    inputChange = () => {
        // 获取DOM数据
        console.log(this.inputMsg.current.value)
    }

    render () {
        return (
            <div>
                {/* 绑定DOM容器 */}
                <input type="text" ref={this.inputMsg} onChange={this.inputChange} />
            </div>
        )
    }
}

function App () {
    return (
        <div>
            <InputComponent></InputComponent>
        </div>
    )
}

export default App

React组件通信

组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)

组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据

为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信

  1. 父子关系 - 最重要的
  2. 兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信
  3. 其它关系 - mobx / redux / zustand
父子传参
  1. 父组件提供要传递的数据 - state

  2. 给子组件标签添加属性值为 state中的数据

  3. 子组件中通过 props 接收父组件中传过来的数据

    • 类组件使用this.props获取props对象
    • 函数式组件直接通过参数获取props对象
import React from 'react'
import Son from './Son'

// props是一个对象,里面存放着父组件传递过来的数据
function SonFunction ({ msg, list, user, fun, child }) {
	// console.log(props)
	// 对父组件传递过来的props进行结构赋值
	// const { msg, list, user, fun, child } = props
	return (
		<div>
			函数组件:{msg}
			<ul>
				{list.map(item => <li key={item}>{item}</li>)}
			</ul>
			<span>user: {user.name} {user.age} {user.address}</span>
			<button onClick={fun}>点击</button>
			{child}
		</div>
	)
}

class App extends React.Component {

	state = {
		msg: 'this is parentComponent message',
		list: [1, 2, 3, 4],
		user: {
			name: 'jsl',
			age: 19,
			address: '河南郑州'
		}
	}

	fun = () => {
		console.log('this is function')
	}

	render () {
		return (
			<div>
				<Son msg={this.state.msg}></Son>
				{/* 子组件身上绑定属性,属性名可以自定义,保持语义化 */}
				<SonFunction
					msg={this.state.msg}
					list={this.state.list}
					user={this.state.user}
					fun={this.fun}
					child={<span>this is span</span>}
				></SonFunction>
			</div>
		)
	}
}

export default App



import React from 'react'

export default class Son extends React.Component {
  render() {
    // 类组件需要通过this.props去获取父组件传递过来的数据
    return <div>类组件: {this.props.msg}</div>
  }
}

props说明

1. props是只读对象(readonly)

根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改

2. props可以传递任意数据

数字、字符串、布尔值、数组、对象、函数、JSX

子父传参
  1. 父组件提供一个回调函数 - 用于接收数据
  2. 将函数作为属性的值,传给子组件
  3. 子组件通过props调用 回调函数
  4. 将子组件中的数据作为参数传递给回调函数
import React from 'react'

// 子传父:子组件调用父组件传递过来的函数,并把想要的数据当成函数的实参传递过去即可
function SonFunction (props) {

	return (
		<div>
			<button onClick={() => props.getSonMessage('sonComponent message')}>click</button>
		</div>
	)
}

class App extends React.Component {

	// 准备一个函数传递给子组件
	getSonMsg = (msg) => {
		console.log('父组件打印:' + msg)
	}

	render () {
		return (
			<div>
				{/* 子组件身上绑定属性,属性名可以自定义,保持语义化 */}
				<SonFunction
					getSonMessage={this.getSonMsg}
				></SonFunction>
			</div>
		)
	}
}

export default App
兄弟组件传参

子组件A先 通过 子父传 参把数据传递给 父组件;父组件 在通过 父子传参,把数据传递给 子组件B

import React from 'react'

// 兄弟间参数传递,父组件作为中转
// 需要传参的组件,子组件A先 通过 子父传 参把数据传递给 父组件;父组件 在通过 父子传参,把数据传递给 子组件B

function SonA (props) {

	return (
		<div>
			this is SonA:B中msg为:{props.sonBMsg}
		</div>
	)
}

function SonB (props) {

	const msg = '这是组件B中的数据'

	function clickHandler () {
		props.getMsg(msg)
	}

	return (
		<div>
			this is SonB
			<button onClick={clickHandler}>click</button>
		</div>
	)
}

class App extends React.Component {

	state = {
		msg: ''
	}

	getSonBMsg = (msg) => {
		this.setState({
			msg: msg
		})
	}

	render () {
		return (
			<div>
				{/* 子组件身上绑定属性,属性名可以自定义,保持语义化 */}
				<SonA sonBMsg={this.state.msg} />
				<SonB getMsg={this.getSonBMsg} />
			</div>
		)
	}
}

export default App
跨组件通信Context(爷孙 通信)
import React, { createContext } from 'react'
// 1. 执行结构获取 提供者和消费者
const { Provider, Consumer } = new createContext()

// 跨组件传递数据
// 注意事项:上层组件和下层组件间的关系是相对的,通常我们都会通过APP作为数据的提供方
// 涉及到的语法都是固定的

function A () {


	return (
		<div>
			this is A
			<C />
		</div>
	)
}

function C () {
	return (
		<div>this is C
			{/* 使用 Consumer 获取数据 */}
			<Consumer>{value => <span>{value}</span>}</Consumer>
		</div>
	)
}

class App extends React.Component {

	state = {
		msg: 'hello'
	}

	render () {
		return (
			// 2. 使用 Provider 包裹上层根组件提供数据
			<Provider value={this.state.msg}>
				<div>
					{/* 子组件身上绑定属性,属性名可以自定义,保持语义化 */}
					<A />
				</div>
			</Provider>
		)
	}
}

export default App
组件children属性

children可以是

  1. 普通文本
  2. 普通标签元素
  3. 函数 / 对象
  4. JSX
import React from 'react'


// 组件小练习,字符传参
function ListComponent ({ children }) {
	// children()
	return (
		<>
			{children}
		</>
	)
}

class App extends React.Component {

	render () {
		return (
			<div>
				<ListComponent>
					{/* hello */}
					{/* <div>你好</div> */}
					{/* {() => console.log('hello')} */}
					{
						<div>
							<p>{'hello'}</p>
						</div>
					}
				</ListComponent>
			</div>
		)
	}
}

export default App
props类型校验

对于组件来说,props是由外部传入的,我们其实无法保证组件使用者传入了什么格式的数据,如果传入的数据格式不对,就有可能会导致组件内部错误,有一个点很关键 - 组件的使用者可能报错了也不知道为什么

四种常见结构

  1. 常见类型:array、bool、func、number、object、string
  2. React元素类型:element
  3. 必填项:isRequired
  4. 特定的结构对象:shape({})
import React from 'react'

// props类型校验,安装 prop-types包,里面有各种各样的内置校验规则
import PropTypes from 'prop-types'

function ListComponent ({ list }) {

	return (
		<>
			{list.map(item => <p>{item}</p>)}
		</>
	)
}

ListComponent.propTypes = {
	// 定义各种校验规则
	list: PropTypes.array.isRequired
}

class App extends React.Component {

	render () {
		return (
			<div>
				<ListComponent list={[1, 2, 3, 6]} />
			</div>
		)
	}
}

export default App
// 常见类型
optionalFunc: PropTypes.func,
// 必填 只需要在类型后面串联一个isRequired
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
	color: PropTypes.string,
	fontSize: PropTypes.number
})

官网文档更多阅读:https://reactjs.org/docs/typechecking-with-proptypes.html

props 默认值
import React from 'react'

// 2. 函数参数默认值(推荐写法)
// 二者区别:第一种在组件使用的时候内部就已经有了pageSize,第二种只有组件传递的时候组件内部才会有pageSize
function ListComponent ({ pageSize = 99 }) {

	return (
		<>
			{pageSize}
		</>
	)
}

// props默认值,父组件没有传参则以默认为主
// 1. 使用defaultProps
ListComponent.defaultProps = {
	pageSize: 10
}

// 类组件 props默认值设置
class List extends React.Component {
	// 默认值设置方法二(推荐)
	static defaultProps = {
		pageSize: 222
	}

	render () {
		return (
			<div>
				类组件List{this.props.pageSize}
			</div>
		)
	}
}
// 默认值设置方法一
List.defaultProps = {
	pageSize: 111
}

class App extends React.Component {

	render () {
		return (
			<div>
				<ListComponent pageSize={100} />
				<List pageSize={333}></List>
			</div>
		)
	}
}

export default App

UI 组件库 Ant-Design

参考网址

React声明周期

组件的生命周期是指组件从被创建到挂载到页面中运行起来,再到组件不用时卸载的过程,注意,只有类组件才有生命周期(类组件 实例化 函数组件 不需要实例化)
在这里插入图片描述

import React from 'react'
// React 生命周期

// 测试组件卸载
class Test extends React.Component {

	// 如果数据是组件的状态需要影响视图,定义在state里面
	// 而如果需要的数据不和视图绑定,定义成一个普通的实例属性就可以了
	// state中的数据尽量精简

	timer = null
	num = 0

	componentDidMount () {
		this.timer = setInterval(() => {
			console.log('开启定时器' + this.num)
			this.num++
		}, 1000)
	}

	// 触发时机:组件卸载(从页面中消失)
	componentWillUnmount () {
		console.log('componentWillUnmount, 执行清理工作(比如: 清理定时器等)')
		clearInterval(this.timer)
	}

	render () {
		return (
			<div>this is Test</div>
		)
	}
}

class App extends React.Component {

	constructor() {
		super()
		// 1. 触发时机:组件创建时最先执行,初始化的时候只执行一次
		console.log('constructor,1. 初始化state 2. 创建Ref 3. 使用bind解决this指向问题(不常用)')
	}

	state = {
		count: 0,
		flag: true
	}

	// 组件挂在完毕
	componentDidMount () {
		// 3. 触发时机:组件挂在(完成DOM渲染)执行,初始化的时候执行一次
		console.log('componentDisMount,1. 发送网络请求 2. DOM操作(常用)')
	}

	// 组件更新
	componentDidUpdate () {
		// 触发时机:组件更新后(DOM渲染完毕)
		console.log('componentDidUpdate,DOM操作, 可以获取更新后的DOM内容, 不要直接调用setState')
	}

	clickHandler = () => {
		this.setState({
			count: this.state.count + 1
		})
	}

	unmountComponent = () => {
		this.setState({
			flag: !this.state.flag
		})
	}

	render () {
		// 2. 触发时机:每次组件渲染都会触发,不能在里面调用setState,会陷入渲染循环(因为调用setState就会触发渲染)
		console.log('render,渲染UI')
		return (
			<div>
				<p>this is div</p>
				<button onClick={this.clickHandler}>{this.state.count}</button>
				{/* 组件 销毁重建 */}
				<button onClick={this.unmountComponent}>组件显示/隐藏</button>
				{this.state.flag ? <Test /> : null}
			</div>
		)
	}
}

export default App

Hooks

Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”

先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks来了

useState

useState为函数组件提供状态(state)

  1. 导入 useState 函数
  2. 调用 useState 函数,并传入状态的初始值
  3. useState函数的返回值中,拿到状态和修改状态的方法
  4. 在JSX中展示状态
  5. 调用修改状态的方法更新状态
import { useState } from "react"

// useState
// 1. 导入useState函数 从react
// 2. 在函数组件中使用这个函数并传入初始参数
// 3. [数据, 修改数据的方法]
// 4. 使用数据,修改数据

// 状态的读取和修改
// const [count, setCount] = useState(0)
// 1. useState传递过来的参数作为解构后第一个属性的初始值
// 2. useState返回的是一个数组,解构的属性名可以自定义,保持语义化
// 3. 解构的第一个为数据状态,第二个为修改状态的函数

// 组件的更新
// 当调用setCount的时候更新过程

// 首次渲染:组件内部的代码会被执行一次,初始值只在首次渲染时生效
// 更新渲染:调用setCount后,组件会再次渲染,函数会再次执行,count的值为修改后的新值

function App () {

  // count: 数据状态
  // setCount: 修改count的函数(count的专有函数)
  const [count, setCount] = useState(0)
  // console.log(count)
  const [flag, setFlag] = useState(true)
  const [list, setList] = useState([1, 2, 3, 4])


  function test () {
    setCount(2)
    setFlag(false)
    setList(list.filter(item => item > 1))
  }

  return (
    <div className="App">
      <p>count: {count}</p>
      <p>flag: {flag ? '1' : '0'}</p>
      <p>list: {list.join('-')}</p>
      <button onClick={test}>按钮</button>
    </div>
  )
}

export default App

useState 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态

函数作为参数

使用场景

参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过计算才能获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用

语法选择

  1. 如果就是初始化一个普通的数据 直接使用 useState(普通数据) 即可
  2. 如果要初始化的数据无法直接得到需要通过计算才能获取到,使用useState(()=>{})
import { useState } from "react"

function Counter (props) {

	const [count, setCount] = useState(() => {
		// 数据无法直接确定,需要计算可以在这里执行
		return props.count
	})

	return (
		<div>
			<button onClick={() => setCount(count + 1)}>{count}</button>
		</div>
	)
}

function App () {

	return (
		<div>
			<Counter count={111} />
		</div>
	)
}

export default App
useEffect

副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)

常见的副作用

  1. 数据请求 ajax发送
  2. 手动修改dom
  3. localstorage操作

useEffect函数的作用就是为react函数组件提供副作用处理的!

import { useState, useEffect } from "react"

// 导入useEffect函数
// 在函数组件中执行,并传入定义副作用的回调
// 修改状态更新组件时,副作用也会执行

// 依赖项控制副作用的执行时机
// 1. 默认状态(无依赖项)
//    组件初始化的时候执行一次,每次修改状态更新组件时执行一次
// 2. 特定依赖项
// 依赖项的状态改变时,副作用执行一次
// (其实默认没有依赖项 就是监控所有的状态,数组为空的时候,只在组件初始化的时候执行一次)
// 注意事项:只要在useEffect回调函数中用到的数据状态,就应该出现在特定依赖中,否则可能有BUG

function App () {

  const [count, setCount] = useState(9)
  const [name, setName] = useState('jsl')

  useEffect(() => {
    // 定义副作用
    console.log('副作用执行了')
    document.title = count
  }, [name, count])

  return (
    <div className="App">
      <p>count: {count}</p>
      <p>name: {name}</p>
      <button onClick={() => setCount(count + 1)}>按钮</button>
      <button onClick={() => setName('hello')}>按钮2</button>
    </div>
  )
}

export default App

清除副作用

如果想要清理副作用 可以在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑

注意执行时机为:

  1. 组件卸载时自动执行
  2. 组件更新时,下一个useEffect副作用函数执行之前自动执行
import { useEffect, useState } from "react"

function Test () {

	useEffect(() => {
		let timer = setInterval(() => {
			console.log('定时器执行')
		}, 1000)
		return () => {
			// 清除副作用
			clearInterval(timer)
		}
	})

	return (
		<div>Test</div>
	)
}

function App () {

	const [flag, setFlag] = useState(true)

	return (
		<div>
			{flag && <Test />}
			<button onClick={() => setFlag(!flag)}>展示/销毁</button>
		</div>
	)
}

export default App
发送网络请求

不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回

可以在内部单独定义一个函数,然后把这个函数包装成同步

import { useEffect } from "react"

function App () {

	// useEffect
	// 1. 不加依赖项 - 初始化+数据状态改变
	// 2. +[] - 初始化执行一次
	// 3. 加上特定依赖项 - 首次加载 + 任意一个依赖项发生变化

	useEffect(() => {
		async function loadDate () {
			// 发送请求操作
			const res = 'this.axios.get(https://www.......)'
			console.log(res)
		}
		loadDate()
	}, [])

	return (
		<div>

		</div>
	)
}

export default App
useRef

在函数组件中获取真实的dom元素对象或者是组件对象

  1. 导入 useRef 函数
  2. 执行 useRef 函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例)
  3. 通过ref 绑定 要获取的元素或者组件
import React, { useEffect, useRef } from "react"

class Test extends React.Component {
	render () {
		return (
			<div>this is Test</div>
		)
	}
}

function App () {

	const testRef = useRef(null)
	const h3Ref = useRef(null)

	// useEffect回调,是在DOM渲染之后执行的
	// 和vue里面的watch很像,但是执行的时机是不同的
	useEffect(() => {
		console.log(testRef.current)
		console.log(h3Ref.current)
	}, [])

	return (
		<div>
			<Test ref={testRef} />
			<h3 ref={h3Ref}>hello</h3>
		</div>
	)
}

export default App
useContext
  1. 使用createContext 创建Context对象
  2. 在顶层组件通过Provider 提供数据
  3. 在底层组件通过useContext函数获取数据
import React, { createContext, useState, useContext } from "react"

const Context = createContext()

function TestA () {
	const count = useContext(Context)
	return (
		<div>
			this is TestA{count}
			<TestC />
		</div>
	)
}

function TestC () {
	const count = useContext(Context)
	return (
		<div>
			this is TestC{count}
		</div>
	)
}

function App () {

	const [count, setCount] = useState(111)

	return (
		<Context.Provider value={count}>
			<div>
				this is App
				<TestA />
				<button onClick={() => setCount(count + 1)}>按钮</button>
			</div>
		</Context.Provider>

	)
}

export default App
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多喝热水,重启试试

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值