基础

创建项目

借助脚手架,新建一个React项目

npx create-react-app 项目名

  • create-react-app是React脚手架的名称

启动项目

npm start 或者 yarn start

目录
  • src是源文件
  • index.js相当于Vue的main.js文件。整个程序的入口
  • App.js相当于Vue的App.js,根组件
{}表达式
  • 里面可以写入方法,变量和三元等js表达式
  • 表达式是可以产生一个值的js语句
export default function App() {
const [num, setNum] = useState(100) const [gender, setGender] = useState(0) const print = () => {
console.log('123') } return ( <div> {
/* {}中可以填入变量 */} 成绩:{
num} {
/* {}中可以填入三元 */} 性别:{
gender === 0 ? '女' : '男'} {
/* {}中还可以写入方法 */} 方法:{
print()} </div> ) }
列表渲染
  • 通过map进行遍历,里面需要绑定key值,方便diff算法进行对比,提高diff性能
  • 重复渲染那个模板,就return
  • key 在当前列表中要唯一的字符串或者数值(String/Number)
export default function App() {
const [list, setList] = useState([ {
id: 0, name: '张三' }, {
id: 1, name: '李四' }, {
id: 2, name: '王五' }, ]) return ( <div> {
list.map((item) => ( <p key={
item.id}>姓名:{
item.name}</p> ))} </div> ) }
条件渲染
  • 根据是否满足条件生成HTML结构,比如Loading效果
  • 可以使用 三元运算符逻辑与(&&)运算符逻辑或(||)运算符
export default function App() {
const [flag, setFlag] = useState(true) return ( <div> {
flag ? '正确的' : null} {
flag && '前面只有为true的情况下才会执行'} {
!flag || '前面只有为false的情况下才会执行'} </div> ) }

if系列判断渲染

  • 可以声明一个方法,接收值,内部进行判断,并返回对应的结果
export default function App() {
const getType = (type) => {
if (type === 0) {
return <span>111</span> } else if (type === 1) {
return <span>222</span> } else if (type === 2) {
return <span>333</span> } } return ( <div> {
getType(1)} </div> ) }
样式处理
  • 利用className指定类名,适合样式比较多的情况

  • 直接行内样式,适合样式比较少的

<h2 style={
{
color: 'red' }}>标题</h2>
  • 单独声明一个样式类名对象
export default function App() {
let style = {
color: 'pink', fontSize: 20, } return ( <div> <h2 style={
style}>标题</h2> </div> ) }

动态类名

  • 根据条件显示类名
export default function App() {
let style = {
color: 'pink', fontSize: 20, } let [flag, setFlag] = useState(true) return ( <div> <h2 style={
flag ? style : ''}>标题</h2> </div> ) }
注意事项
  1. JSX必须有一个根节点,如果没有根节点,可以使用<></>(幽灵节点)替代
  2. 所有标签必须形成闭合,成对闭合或者自闭合都可以
  3. JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class -> className for -> htmlFor
  4. JSX支持多行(换行),如果需要换行,需使用() 包裹,防止bug出现
小案例1
  • 实现一个最基本的评论(不完整)
import './index.css'
import avatar from './images/avatar.png'
// 依赖的数据
const state = {
// hot: 热度排序 time: 时间排序 tabs: [ {
id: 1, name: '热度', type: 'hot', }, {
id: 2, name: '时间', type: 'time', }, ], active: 'hot', list: [ {
id: 1, author: '刘德华', comment: '给我一杯忘情水', time: new Date('2021-10-10 09:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: 1, }, {
id: 2, author: '周杰伦', comment: '哎哟,不错哦', time: new Date('2021-10-11 09:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: 0, }, {
id: 3, author: '五月天', comment: '不打扰,是我的温柔', time: new Date('2021-10-11 10:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: -1, }, ], } // 时间格式化 const format = (time) => {
return `${
time.getFullYear()}
-${

time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1
}
-${
time.getDate() < 10 ? '0' + time.getDate() : time.getDate()}
${

time.getHours() < 10 ? '0' + time.getHours() : time.getHours()
}
:${
time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}
:${

time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()
}
`
} // tab切换 const activeClick = (active) => {
state.active = active } function App() {
return ( <div className="App"> <div className="comment-container"> {
/* 评论数 */} <div className="comment-head"> <span>5 评论</span> </div> {
/* 排序 */} <div className="tabs-order"> <ul className="sort-container"> {
state.tabs.map((item) => {
return ( <li className={
state.active === item.type ? 'on' : ''} onClick={
() => activeClick('hot')} key={
item.id} >{
item.name}排序 </li> ) })} </ul> </div> {
/* 添加评论 */} <div className="comment-send"> <div className="user-face"> <img className="user-head" src={
avatar} alt="" /> </div> <div className="textarea-container"> <textarea cols="80" rows="5" placeholder="发条友善的评论" className="ipt-txt" /> <button className="comment-submit">发表评论</button> </div> <div className="comment-emoji"> <i className="face"></i> <span className="text">表情</span> </div> </div> {
/* 评论列表 */} <div className="comment-list"> {
state.list.map((item) => {
return ( <div className="list-item" key={
item.id}> <div className="user-face"> <img className="user-head" src={
avatar} alt="" /> </div> <div className="comment"> <div className="user">{
item.author}</div> <p className="text">{
item.comment}</p> <div className="info"> <span className="time">{
format(item.time)}</span> <span className={
item.attitude === 1 ? 'like liked' : 'like'} > <i className="icon" /> </span> <span className={
item.attitude === -1 ? 'hate hated' : 'hate'} > <i className="icon" /> </span> <span className="reply btn-hover">删除</span> </div> </div> </div> ) })} </div> </div> </div> ) } export default App

组件

  • 分为函数式组件(rfc)和类组件(rnc)
  • 安装ES7+ React/Redux/React-Native snippets这个插件后就可以使用上述指令快速创建组件
  • 主要讲函数式组件
绑定事件
  • on开头,后面紧跟事件名(事件名首字母大写)

on事件名 = { 事件处理函数名 } // 无参

on事件名 = { () => 事件处理函数名(参数1,参数2…) } // 有参

on事件名 = { (e) => 事件处理函数名(e,参数2…) } // 有参,带e的

  • 事件处理函数

let/const 事件处理函数名 = (参数) => { … }

eg:

import React from 'react'

export default function App() {
const print = () => {
console.log('无参的') } const hasParams = (e, num) => {
console.log('有参的', e, num) } return ( <div> <button onClick={
print}>print</button> <button onClick={
(e) => hasParams(e, '123')}>hasParams</button> </div> ) }
小技巧
  • 数值改变
  • 数组添加
  • 对象修改
import React from 'react'
import {
useState } from 'react' export default function App() {
const [num, setNum] = useState(10) const [list, setList] = useState([]) const [obj, setObj] = useState({
name: '张三', }) // 数值加几,可以直接在后面写加几 const numAdd = (n) => {
setNum(num + n) } // 数组添加,可以直接在尾部添加 const listAdd = (item) => {
setList([...list, item]) } // 修改对象中的某一项 const objEdit = (val) => {
setObj({
...obj, // 下面的会覆盖上面的同名的属性,达到修改的目的 name: val, }) } return ( <div> <button onClick={
() => numAdd(1)}>数值加1--{
num}</button> <div> <button onClick={
() => listAdd('数组新的一项')}>数组添加一项</button> {
list.map((item, i) => ( <p key={
i}>{
item}</p> ))} </div> <div> <button onClick={
() => objEdit('李四')}> 修改对象的某一项(修改name) </button> <p>{
obj.name}</p> </div> </div> ) }
  • 数组删除(最好利用filter)
import React from 'react'
import {
useState } from 'react' export default function App() {
const [list, setList] = useState([1,2,3]) // 删除数组中下标为2的内一项 const delItem = (index) => {
let newList = list.filter((item, i) => i !== 2) setList(newList) // 或者直接操作,也是可以的 // setList(list.filter((item, i) => i !== 2)) } return ( <div> <button onClick={
() => delItem(2)}>删除数组中的某一项</button> </div> ) }
受控组件
  • 被react状态控制的组件就叫受控组件。通过事件对象e,可以获取输入框中的值
import React from 'react'
import {
useState } from 'react' export default function App() {
const [val, setVal] = useState('') // 表单里面的值发生变化 const onChange = (e) => {
// 获得输入框中的值 console.log(e.target.value) // 赋值给val setVal(e.target.value) } return ( <div> <input type="text" name="" id="" value={
val} onChange={
onChange} /> </div> ) }
非受控组件
  • 不受react状态控制的组件叫非受控组件。通过获取dom元素,来获取输入框中的值
import React from 'react'
import {
useRef } from 'react' export default function App() {
const ipt = useRef(null) // 表单里面的值发生变化 const onChange = () => {
// 获得输入框中的值 console.log(ipt.current.value) } return ( <div> <input type="text" name="" id="" ref={
ipt} onChange={
onChange} /> </div> ) }
小案例2
  • 完整的评论功能
import './index.css'
import avatar from './images/avatar.png'
import {
useState } from 'react' // 时间格式化 const format = (time) => {
return `${
time.getFullYear()}
-${

time.getMonth() + 1 < 10 ? '0' + time.getMonth() + 1 : time.getMonth() + 1
}
-${
time.getDate() < 10 ? '0' + time.getDate() : time.getDate()}
${

time.getHours() < 10 ? '0' + time.getHours() : time.getHours()
}
:${
time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()}
:${

time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()
}
`
} function App() {
// hot: 热度排序 time: 时间排序 const [tabs] = useState([ {
id: 1, name: '热度', type: 'hot', }, {
id: 2, name: '时间', type: 'time', }, ]) const [list, setList] = useState([ {
id: 1, author: '刘德华', comment: '给我一杯忘情水', time: new Date('2021-10-10 09:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: 1, }, {
id: 2, author: '周杰伦', comment: '哎哟,不错哦', time: new Date('2021-10-11 09:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: 0, }, {
id: 3, author: '五月天', comment: '不打扰,是我的温柔', time: new Date('2021-10-11 10:09:00'), // 1: 点赞 0:无态度 -1:踩 attitude: -1, }, ]) // 切换的tab const [active, setActive] = useState('hot') // tab切换 const activeClick = (type) => {
setActive(type) } // 输入框的值 const [iptVal, setIptVal] = useState('') // 得到输入框中的值 const getVal = (e) => {
setIptVal(e.target.value) } // 点击发送评论按钮 const sendCommit = () => {
if (!iptVal || iptVal.trim().length < 1) {
return alert('输入不能为空或都是空格') } setList([ ...list, {
id: +new Date(), author: '孤勇者', comment: iptVal, time: new Date(), // 1: 点赞 0:无态度 -1:踩 attitude: 0, }, ]) setIptVal('') } // 点击删除 const delItm = (id) => {
let newList = list.filter((item) => item.id !== id) setList(newList) } // 点击点赞/点踩 const toggleMood = (item) => {
let {
id, attitude } = item let newList = list.map((item) => {
if (item.id === id) {
return {
...item, attitude: attitude === 1 ? 0 : 1, } } else {
return item } }) console.log(newList) setList(newList) } return ( <div className="App"> <div className="comment-container"> {
/* 评论数 */} <div className="comment-head"> <span>{
list.length} 评论</span> </div> {
/* 排序 */} <div className="tabs-order"> <ul className="sort-container"> {
tabs.map((item) => {
return ( <li className={
active === item.type ? 'on' : ''} onClick={
() => activeClick(item.type)} key={
item.id} >{
item.name}排序 </li> ) })} </ul> </div> {
/* 添加评论 */} <div className="comment-send"> <div className="user-face"> <img className="user-head" src={
avatar} alt="" /> </div> <div className="textarea-container"> <textarea cols="80" rows="5" placeholder="发条友善的评论" className="ipt-txt" onChange={
getVal} value={
iptVal} /> <button className="comment-submit" onClick={
sendCommit}> 发表评论 </button> </div> <div className="comment-emoji"> <i className="face"></i> <span className="text">表情</span> </div> </div> {
/* 评论列表 */} <div className="comment-list"> {
list.map((item, index) => {
return ( <div className="list-item" key={
item.id}> <div className="user-face"> <img className="user-head" src={
avatar} alt="" /> </div> <div className="comment"> <div className="user">{
item.author}</div> <p className="text">{
item.comment}</p> <div className="info"> <span className="time">{
format(item.time)}</span> <span className={
item.attitude === 1 ? 'like liked' : 'like'} onClick={
() => toggleMood(item)} > <i className="icon" /> </span> <span className={
item.attitude === -1 ? 'hate hated' : 'hate'} > <i className="icon" /> </span> <span className="reply btn-hover" onClick={
() => delItm(item.id)} > 删除 </span> </div> </div> </div> ) })} </div> </div> </div> ) } export default App

组件通信

  • 父子通信,子父通信,非父子通信
父->子通信
  • 父组件在子组件标签上绑定要传入的数据(会默认添加到props中),子组件通过props进行使用

父组件

import React from 'react'
import {
useState } from 'react' import Son from './pages/Son.jsx' export default function App() {
const [num, setNum] = useState(100) return ( <div> <h2>App</h2> <p>下面是子组件</p> <Son num={
num}></Son> </div> ) }

子组件

import React from 'react'

export default function Son(props) {
return ( <div> <h2>Son</h2> <p>从父组件传来的值是:{
props.num}</p> </div> ) }
props详解
  1. props是只读对象(readonly)


    • 是自顶向下单向数据流,根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改
  2. props可以传递任意数据


    • 数字、字符串、布尔值、数组、对象、函数(多用子向父传值)JSX(类似于插槽)

父组件

import React,{
useState } from 'react' import Son from './pages/Son.jsx' export default function App() {
// 数字 const [num, setNum] = useState(100) // 字符 const [str, setStr] = useState('str') // 布尔 const [bool, setBool] = useState(false) // 数组 const [list, setList] = useState([1, 2, 3]) // 对象 const [obj, setObj] = useState({
name: '张三', age: 24 }) // 函数 const print = () => {
return 'print' } // jsx const jsx = <span>123</span> return ( <div> <h2>App</h2> <p>下面是子组件</p> <Son num={
num} str={
str} bool={
bool} list={
list} obj={
obj} print={
print} jsx={
jsx} > 直接写在标签内的jsx结构,会自动传入到props中的children属性里(或者子组件标签上写children属性,一样的效果) </Son> </div> ) }

子组件

import React from 'react'
// 也可以直接在参数这里解构
export default function Son(props) {
let {
num, str, bool, list, obj, print, jsx } = props return ( <div> <h2>Son</h2> <p>从父组件传来的值是:{
num}</p> <p>从父组件传来的值是:{
str}</p> <p>从父组件传来的值是:{
bool ? '是true' : '是false'}</p> <p>从父组件传来的数组,渲染结果如下</p> <ul> {
list.map((item) => ( <li key={
item}>{
item}</li> ))} </ul> <p>从父组件传来的对象,渲染结果如下</p> {
obj.name} --- {
obj.age} <p>从父组件传来的函数,渲染结果如下</p> {
print()} <p>父组件传来的jsx结构,如下</p> {
jsx} <p>父组件传过来的jsx结构,如下</p> {
props.children} </div> ) }
子->父通信
  • 也是通过props,通过传递函数进行通信
  • 子组件调用父组件传递过来的函数,并且把想要传递的数据当成函数的实参

子组件

import React,{
useState } from 'react' export default function Son(props) {
let [msg, setMsg] = useState('子组件向父组件传递的数据') const sendMsg = () => {
props.getMsg(msg) } return ( <div> <h2>Son</h2> <button onClick={
sendMsg}>点击向父组件传值</button> </div> ) }

父组件

import React,{
useState } from 'react' import Son from './pages/Son' export default function App() {
let [sonData, setSonData] = useState({
}) const getMsg = (val) => {
console.log(val) setSonData({
...sonData, msg: val }) } return ( <div> <h2>App --- {
sonData.msg}</h2> <Son getMsg={
getMsg}></Son> </div> ) }
兄弟组件通信
  • 通过状态提升,利用共同的父组件实现兄弟通信
  • 兄弟组件A -> 父组件 -> 兄弟组件B

子组件A

import React, {
useState } from 'react' export default function SonA(props) {
const [msgA, setMsgA] = useState('兄弟组件A传递的数据') const sendB = () => {
props.getMsgA(msgA) } return ( <div> <h3>Son1</h3> <button onClick={
sendB}>点击发送给兄弟组件B</button> </div> ) }

父组件

import React, {
useState } from 'react' import SonA from './pages/SonA' import SonB from './pages/SonB' export default function App() {
const [msgA, setMsgA] = useState('') // 接收A组件传来的值 const getMsgA = (val) => {
setMsgA(val) } return ( <div> <h2>App</h2> <SonA getMsgA={
getMsgA}></SonA> <SonB msgA={
msgA}></SonB> </div> ) }

子组件B

import React from 'react'

export default function SonB(props) {
return ( <div> <h3>Son2</h3> <p>接收兄弟组件B传来的值为:{
props.msgA}</p> </div> ) }
跨组件通信Context
  • 直接在index.js文件中提供数据,则全局都可以使用
    • 适用于只是用1次的静态的数据
  • 如果提供的数据需要维护状态,则写到app.js

使用步骤

  1. 首先创建一个独立的文件,Context.js
import {
createContext } from 'react' const Context = createContext() export default Context
  1. 那个组件需要就直接导入,Provider标签包裹根组件,value提供数据(提供的数据比较多,就可以使用对象形式)
import React, {
useState } from 'react' import Son from './pages/Son' // 1. 引入Context import Context from './utils/context' export default function App() {
const [msg, setMsg] = useState('根组件传递的数据') return ( <> {
/* 2. 使用Provider包裹上层组件提供数据 */} <Context.Provider value={
msg}> {
/* 根组件 */} <div> <h2>App</h2> <Son></Son> </div> </Context.Provider> </> ) }
  1. 数据消费组件,用useContext这个hook,或者通过Consumer标签接收显示数据

使用useContext这个hook

import React, {
useContext } from 'react' import Sun from './Sun' import Context from '../utils/context' export default function Son() {
let val = useContext(Context) return ( <div> <h3>Son</h3> <p>从根组件得到的数据 --- {
val}</p> <Sun></Sun> </div> ) }

通过Consumer标签

import React from 'react'
import Context from '../utils/context'

export default function Sun() {
return ( <div> <h5>Sun</h5> <div> 从根组件得到的数据: <Context.Consumer>{
(value) => <span>{
value}</span>}</Context.Consumer> </div> </div> ) }

组件进阶

children属性
  • 表示该组件的子节点,只要组件内部有子节点,props中就有该属性

  • children属性,类似于插槽。直接写在标签中的内容会填充到children属性上面

  • children可以是普通文本普通标签元素函数 / 对象JSX

  • 如果并列的传入多个,propschildren属性会变成一个数组(就可以直接进行遍历)

父组件

import React from 'react'
import Son from './pages/Son'

export default function App() {
return ( <div> <h2>App</h2> <Son> 普通文本:666 <div>普通标签元素</div> {
/* 函数 */} {
function fn() {
console.log('函数打印') }} {
/* JSX结构 */} {
<div> <p>{
'这是一个普通的jsx结构'}</p> </div> } </Son> </div> ) }

子组件

import React from 'react'

export default function Son(props) {
console.log(props) return ( <div> <h3>Son</h3> <p>{
props.children[0]}</p> {
props.children[1]} {
props.children[2]()} {
props.children[3]} <hr /> {
props.children.map((item) => {
return item })} </div> ) }
props校验
  1. 下载 prop-types 插件 ,并导入prop-types包 yarn add prop-types
  2. 使用 组件名.propsTypes = { } 来给组件的props中的数据添加校验规则
  3. 校验规则通过PropTypes对象来指定

检验基本语法

组件名.prototype = {

属性名 : PropTypes.XXX,
}

  • PropTypes是引入的prop-types插件的实例

设置默认值

组件名.defaultProps= {

属性名 : 默认值,
}

  • 或者在参数上直接给默认值

父组件

import React, {
useState } from 'react' import Son from './pages/Son' export default function App() {
const [list] = useState([ {
id: 0, name: '张三', }, {
id: 1, name: '李四', }, ]) const [obj] = useState({
name: '王五', age: 24, }) return ( <div> <h2>App</h2> <Son list={
list} score={
100} obj={
obj}></Son> </div> ) }

子组件

import React from 'react'
import PropTypes from 'prop-types'

export default function Son(props) {
let {
list, score, obj } = props return ( <div> <h3>Son</h3> <ul> {
list.map((item) => ( <li key={
item.id}>{
item.name}</li> ))} </ul> <p>成绩是:{
score}</p> <p>姓名:{
obj.name}</p> <p>年龄:{
obj.age}</p> </div> ) } // 对传过来的值进行校验 Son.propTypes = {
list: PropTypes.array.isRequired, // 也可以自定义校验规则 peops是所有接收过来的数据,propsName是字段名,componentName组件名 score: function (props, propsName, componentName) {
if (props[propsName] < 60) {
return new Error('成绩不合格') } }, obj: PropTypes.shape({
name: PropTypes.string, age: PropTypes.number, }), } // 设置默认值 Son.defaultProps = {
list: [], score: 100, }

常见规则

  • 常见类型: array bool func number object string
  • React元素类型(JSX): element
  • 是否必填: isRequired
  • 特定结构的对象: shape({ }) 也就是指定对象里面字段的规则,可以指定一个,也可以指定多个
// 特定结构的对象
obj: PropTypes.shape({
name: PropTypes.string, age: PropTypes.number, }),
  • 也可以自定义校验规则(见上面的例子)

Hook

useState
  • useState(初始值)返回值是一个数组(里面有两项)
  • [数据,修改数据的方法] 是对 useState进行结构。把里面的两项分别结构出来

格式:

let/const [ 数据 ,修改数据的方法 ] = useState(默认值)

eg:

import React from 'react'
import {
useState } from 'react' export default function App() {
const [count, setCount] = useState(0) console.log(useState(0)) // (2) [0, ƒ] const add = (num) => {
let newCount = count + num setCount(newCount) } return ( <div> <h2>App --- {
count}</h2> <button onClick={
() => add(1)}>+1</button> </div> ) }

函数做为参数

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

格式:

const [name, setName] = useState(()=>{

// 编写计算逻辑 return ‘计算之后的初始值’

})

eg:

父组件

import React from 'react'
import {
useState } from 'react' import Son from './pages/Son' export default function App() {
const [count, setCount] = useState(0) const countEdit = (num) => {
setCount(num) } return ( <div> <h2>App</h2> <button onClick={
() => countEdit(10)}>10</button> <button onClick={
() => countEdit(20)}>20</button> <Son count={
count}></Son> </div> ) }

子组件

import React from 'react'
import {
useEffect } from 'react' import {
useState } from 'react' export default function Son(props) {
const [c, setc] = useState(() => props.count) useEffect(() => {
setc(props.count) }, [props]) return ( <div> <h3>Son -- {
c}</h3> </div> ) }
useEffect
  • useEffect函数的作用就是为react函数组件提供副作用处理的
  • useEffect都是在组件dom渲染更新完毕之后才执行的

副作用

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

常见的副作用

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

执行时机

1.不添加依赖项

  • 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
    1. 组件初始渲染
    2. 组件更新 (不管是哪个状态引起的更新)
useEffect(()=>{
console.log('副作用执行了') })
  1. 添加空数组
  • 组件只在首次渲染时执行一次
useEffect(()=>{
console.log('副作用执行了') },[])
  1. 添加特定依赖项
  • 副作用函数在首次渲染时执行在依赖项发生变化时重新执行
    1. 组件初始渲染
    2. 依赖项发生变化时
function App() {
const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => {
console.log('副作用执行了') }, [count]) return ( <> <button onClick={
() => {
setCount(count + 1) }}>{
count}</button> <button onClick={
() => {
setName('cp') }}>{
name}</button> </> ) }

注意事项

  • useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug出现

清除副作用

  • 在组件被销毁时,如果有些副作用操作需要被清除(比如定时器)

语法:

useEffect(() => {

​ // 副作用操作…

​ return () => {

​ // 写清除副作用的代码

​ }

})

eg: 清除定时器案例

父组件

import React from 'react'
import {
useState } from 'react' import Son from './pages/Son' export default function App() {
const [flag, setFlag] = useState(true) return ( <div> <h2>App</h2> <button onClick={
() => setFlag(!flag)}>显示/隐藏组件</button> {
flag && <Son></Son>} </div> ) }

子组件

import React, {
useEffect } from 'react' export default function Son() {
// 组件进来的时候触发一个定时器 useEffect(() => {
let timer = setInterval(() => {
console.log('定时器执行了') }, 1000) // 组件销毁时清除定时器 return () => {
// 在return里面的函数里写清除操作 clearInterval(timer) } }, []) return ( <div> <h3>Son</h3> </div> ) }

useEffect 发送网络请求

  • 依赖项要是一个空数组,因为依赖项为空数组时只会在页面初始化时触发一次
import React from 'react'
import {
useEffect } from 'react' export default function App() {
const getData = () => {
fetch('https://cnodejs.org/api/v1/topics') .then((response) => response.json()) .then((data) => console.log(data.data)) } useEffect(() => {
getData() }, []) return ( <div> <h2>App</h2> </div> ) }
案例1
  • 求卷去头部距离的hook
import {
useState } from 'react' export default function useWindowScroll() {
const [y, sety] = useState('') window.addEventListener('scroll', function () {
sety(this.document.documentElement.scrollTop) }) return [y] } 使用 import React from 'react' import useWindowScroll from './hook/useWindowScroll' export default function App() {
const [y] = useWindowScroll() return ( <div style={
{
height: 1600 }}> <h2>App -- {
y}</h2> </div> ) }
案例2
  • 数据改变,会同步到本地
import {
useEffect, useState } from 'react' export default function useLocalStorage(key, defaultVal) {
const [val, setVal] = useState(defaultVal) // 只要val发生变化,就同步到本地 useEffect(() => {
localStorage.setItem(key, val) }, [val, key]) return [val, setVal] } 使用 import React from 'react' import useWindowScroll from './hook/useWindowScroll' import useLocalStorage from './hook/useLocalStorage' export default function App() {
const [y] = useWindowScroll() const [val, setVal] = useLocalStorage('val', 0) const add = () => {
setVal(val + 1) } return ( <div style={
{
height: 1600 }}> <h2> App -- {
y} -- {
val} </h2> <button onClick={
add}>+1</button> </div> ) }
useRef
  • 可以获取元素的真实Dom
import React, {
useEffect, useRef } from 'react' export default function App() {
const ipt = useRef(null) useEffect(() => {
console.log(ipt.current.value) }, []) return ( <div> <h2>App</h2> <input type="text" ref={
ipt} /> </div> ) }
useContext
  • 传输的数据是响应式的,跨组件传输数据用
  • 如果传递的数据,只需要在整个应用初始化的时候传递一次就可以,则可以在index.js文件中提供数据
  • 如果传递的数据需要状态维护,则可以在app.js中提供数据

使用步骤

  1. 创建一个context的文件
  2. 使用createContext创建Context对象,并导出
  3. 在顶层组件引入,通过Provider提供数据
  4. 在底层组件引入,通过useContext函数获取数据

举例

context.js

import {
createContext } from 'react' const Context = createContext() export default Context

上层组件

import React, {
useState } from 'react' import Son from './pages/Son' // 1. 引入Context import Context from './utils/context.js' export default function App() {
const [msg] = useState('根组件传递的数据') return ( <> {
/* 2. 使用Provider包裹上层组件提供数据 */} <Context.Provider value={
msg}> {
/* 根组件 */} <div> <h2>App</h2> <Son></Son> </div> </Context.Provider> </> ) }

下层组件

import React, {
useContext } from 'react' import Context from '../utils/context.js' export default function Son() {
let val = useContext(Context) return ( <div> <h3>Son</h3> <p>从根组件得到的数据 --- {
val}</p> </div> ) }

补充

  • document.title 可以获取网页最左上的标题

原文链接:https://blog.csdn.net/qq_52845451/article/details/128171711

最后修改:2023 年 10 月 30 日
如果觉得我的文章对你有用,请随意赞赏