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结构)
优势:
- 采用类似于HTML的语法,降低学习成本,会HTML就会JSX
- 充分利用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 注意事项
- JSX必须有一个根节点,如果没有根节点,可以使用
<></>
(幽灵节点)替代 - 所有标签必须形成闭合,成对闭合或者自闭合都可以
- JSX中的语法更加贴近JS语法,属性名采用驼峰命名法
class -> className
for -> htmlFor
- 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
函数组件说明:
- 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
- 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
- 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
- 使用函数名称作为组件标签名称,可以成对出现也可以自闭合
类组件说明:
- 类名称也必须以大写字母开头
- 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
- 类组件必须提供 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
中,React
将state
与表单元素的值(value)
绑定到一起,由state
的值来控制表单元素的值,从而保证单一数据源特性
- 在组件的state中声明一个组件的状态数据
- 将状态数据设置为input标签元素的value属性的值
- 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(
即用户当前输入的值
) - 调用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获取输入框的值
- 导入
createRef
函数 - 调用createRef函数,创建一个ref对象,存储到名为
msgRef
的实例属性中 - 为input添加ref属性,值为
msgRef
- 在按钮的事件处理程序中,通过
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)
组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信
- 父子关系 - 最重要的
- 兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信
- 其它关系 - mobx / redux / zustand
父子传参
-
父组件提供要传递的数据 -
state
-
给子组件标签
添加属性
值为 state中的数据 -
子组件中通过
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
子父传参
- 父组件提供一个回调函数 - 用于接收数据
- 将函数作为属性的值,传给子组件
- 子组件通过props调用 回调函数
- 将子组件中的数据作为参数传递给回调函数
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可以是
- 普通文本
- 普通标签元素
- 函数 / 对象
- 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是由外部传入的,我们其实无法保证组件使用者传入了什么格式的数据,如果传入的数据格式不对,就有可能会导致组件内部错误,有一个点很关键 - 组件的使用者可能报错了也不知道为什么
四种常见结构
- 常见类型:array、bool、func、number、object、string
- React元素类型:element
- 必填项:isRequired
- 特定的结构对象: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)
- 导入
useState
函数 - 调用
useState
函数,并传入状态的初始值 - 从
useState
函数的返回值中,拿到状态和修改状态的方法 - 在JSX中展示状态
- 调用修改状态的方法更新状态
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,此函数只在初始渲染时被调用
语法选择
- 如果就是初始化一个普通的数据 直接使用
useState(普通数据)
即可 - 如果要初始化的数据无法直接得到需要通过计算才能获取到,使用
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)
常见的副作用
- 数据请求 ajax发送
- 手动修改dom
- 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一个新的函数,在新的函数中编写清理副作用的逻辑
注意执行时机为:
- 组件卸载时自动执行
- 组件更新时,下一个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元素对象或者是组件对象
- 导入
useRef
函数 - 执行
useRef
函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例) - 通过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
- 使用
createContext
创建Context对象 - 在顶层组件通过
Provider
提供数据 - 在底层组件通过
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