这是我参与「第四届青训营 」笔记创作活动的的第4天
React 生命周期
1、hello world程序编写(index.js)
//1、引入React两个核心模块
import React from 'react';
import ReactDOM from 'react-dom';
//2、通过JSX语法将组件/标签渲染到指定标签上
ReactDOM.render(
<h1>
hello world!
</h1>
, document.getElementById('root'));
//ReactDOM.render(参数1,参数2)
//参数1 是JSX语法的标签/组件
//参数2 是要把参数1这个标签渲染到的位置
2、组件化开发(app.js)
//1、导入React核心模块
import React from 'react'
//2、定义组件类
class Hello extends React.Component{ //类
render(){ //函数
return ( //返回值
<div>
hello world !!! 我是组件222
</div>
)
}
}
//3、导出组件
export default Hello
//4、导出并定义组件类
export default class Hello extends React.Component{ //类
render(){ //函数
return ( //返回值
<div>
hello world !!! 我是组件222
</div>
)
}
}
//页面渲染
//import 组件名 from '文件路径'
import Hello from './App'
//1、导入Hello组件 (首字母必须大写)
ReactDOM.render(
<Hello /> // 2、使用Hello组件 (首字母必须大写)
, document.getElementById('root'));
//注意:Hello是组件名,在使用的时候就应该写JSX标签写法,而不能直接写Hello
//!!注意:
//1、定义组件的时候,return 后面只能有一个根标签,不能有多个,但这个标签内部可以有其他多个标签
//2、使用组件的时候,首字母必须大写
//3、如果最外层实在不想放置一层div根目录,可以使用 `<></>`空标签或者`<Fragment></Fragment>` 标签代替
3、JSX注意的格式
1、React的JSX是使用大写和小写字母来区分本地组件和HTML组件
2、JSX和html的标签属性的区别
eg:渲染图片
import React, { Component } from 'react'
var MyImg = require('./assets/01.jpg')
export default class App2 extends Component {
render() {
return (
<>
<label htmlFor="ipt">label</label>
<input type="text" id="ipt" className="ipt" style={{background: 'pink', color: 'green'}} />
<hr/>
<img src={MyImg} alt=""/>
</>
)
}
}
4、JSX变量引用、三目运算符、for循环
import React, { Component } from 'react'
let name = "小明", num1=20, num2=30, arr=[1, 2, 3, 4, 5]
export default class App3 extends Component {
render() {
return (
<div>
{/* 这是注释的格式 */}
{/* JSX中引用变量需要加单花括号 */}
<p>{name}</p>
{/* 三目运算符的使用 */}
<p>num1和num2中比较大的是:{num1>num2? num1: num2}</p>
<p>{gender === 0 ? '男' : (gender === 1 ? '女' : '保密')}</p>
{/* for循环的使用 */}
<ul>
{/* 数组名.map(函数) */}
{
//格式1:
arr.map((v,k)=>(
<li key={k}>{v}</li>
))
}
{
//格式2:可以把下面的大括号和return换成小括号
arr.map((v,k)=>{
return <li key={k}>{v}</li>
})
}
</ul>
</div>
)
}
}
5、事件、state与setState
//事件讲解
import React, { Component } from 'react'
//1、实现点击弹框效果(事件基本格式)
export default class App5 extends Component {
handleClick(){
alert(132456)
}
render() {
return (
<div>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
}
//2、实现累加的功能 (状态的使用1)
export default class App4 extends Component {
constructor(props){
super(props)
this.state = {
num: 0
}
// 按钮3的函数写法的前提
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState({
num: this.state.num+1
})
}
render() {
return (
<div>
<h2>{ this.state.num }</h2>
{/* 通过bind来改变this的指向 */}
{/* <button onClick={this.handleClick.bind(this)}>按钮1</button> */}
{/* 箭头函数默认没有this,所以this指向数组对象 */}
{/* <button onClick={()=>this.handleClick()}>按钮2</button> */}
<button onClick={this.handleClick}>按钮3</button>
</div>
)
}
}
//3、实现双向数据绑定 (状态的使用2)
export default class App5 extends Component {
constructor(p){
super(p)
this.state = {
name: "你好,世界"
}
}
// 写在construtor中的state也可以简写
state = {
name: "你好,世界"
}
handleChange(e){
console.log(e.currentTarget.value)
console.log(e.target.value)
this.setState({
name: e.target.value
})
}
render() {
return (
<div>
<h2>{ this.state.name }</h2>
<input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} />
</div>
)
}
//简写
render() {
return (
<div>
<h1>{this.state.username}</h1>
<button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
)
}
}
5.1 state对数组的修改
import React, { Component } from 'react'
export default class App9 extends Component {
state = {
arr: ["张飞", "赵云", "刘备"]
}
render() {
return (
<div>
<ul>
{
this.state.arr.map((item, index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
<button onClick={this.addFn.bind(this)}>追加一项到arr的最后</button>
</div>
)
}
// 追加一项到arr的最后
// 修改state的方式只能是setState
// 数组的push方法会修改原数组
// this.state.arr.push() 会造成直接修改了state中的arr
// 修改数组,不能直接修改this.state.arr
addFn(){
// 如果我声明一个变量,等于this.state.arr,并且让这个变量和this.state.arr完全脱离关系[深拷贝]
let newArr = JSON.parse(JSON.stringify(this.state.arr));
// 往newArr中push一项
newArr.push('关羽');
// 把最新的newArr赋值给arr
this.setState({
arr: newArr
})
}
}
6、函数式组件
// App.jsx
import React from 'react'
function App(){
return <h2>你好世界</h2>
}
export default App;
//1、没有this,组件中打印this,会得到undefined
//2、没有state,数据的控制不受state管理,需要使用hooks
//3、没有生命周期
6.1 父子传参
6.1.1 父传子
// 父组件
import Sub from './Sub'
let msg = "你好世界"
export default function App(){
return <Sub msg={msg} />
}
// 子组件
export default function Sub(props) {
return <h2>{props.msg}</h2>
}
6.1.2 子传父
// 父组件
import Sub from './Sub'
let msg = "你好世界"
export default function App(){
const fn = function(arg){
console.log(arg) // 123
}
return <Sub msg={msg} fn={fn} />
}
// 子组件
export default function Sub(props) {
return (
<>
<h2>{props.msg}</h2>
<button onClick={()=>props.fn(123)}>将123传递给父组件</button>
</>
)
}
6.2 Hooks
6.2.1 useState--定义响应式数据
import React from 'react'
function App(){
const [num, setNum] = React.useState(1)
return (
<>
<h2>数字为:{num}</h2>
<button onClick={()=>setNum(num+1)}>累加</button>
</>
)
}
export default App;
6.2.2 useEffect--有时我们需要在挂载后请求数据、销毁前清除数据,或是在更新后获取数据,那么就需要借助useEffect。
//模拟挂载后--在挂载后请求数据
import React, {useEffect} from 'react'
export default function App(){
useEffect(()=>{
// do some requests
console.log('挂载后')
})
return <h2>你好世界</h2>
}
//模拟销毁前--垃圾回收或数据清理
import React, {useEffect} from 'react'
export default function App(){
useEffect(()=>{
// do some requests
return ()=>{
// do some clear
console.log('销毁前')
}
})
return <h2>你好世界</h2>
}
//在useEffect的callback中返回一个函数,代表“销毁前”。如果想要体验销毁前,可以在入口文件index.js中添加:
setTimeout(()=>{
ReactDOM.render(
<input type="text" />,
document.getElementById('root')
)
}, 3000)
//模拟检测更新
import React, {useEffect, useState} from 'react'
export default function App(){
const [num1, setNum1] = useState(1)
const [num2, setNum2] = useState(1)
useEffect(()=>{
console.log('num1更新了')
}, [num1])
return (
<>
<h2>数字1:{num1}</h2>
<button onClick={()=>setNum1(num1+1)}>累加num1</button>
<hr />
<h2>数字2:{num2}</h2>
<button onClick={()=>setNum2(num2+1)}>累加num1</button>
</>
)
}
//注意!!
//数组为空,代表不检测数据更新
//数组中填写的变量,才会被检测更新
//如果整个页面所有数据都需要检测更新,那么可以直接不写数组
6.3 createContext
6.3.1 Provider与Consumer--通过createContext创建的上下文空间自带Provider(提供器)与Consumer(接收器)
import React, {createContext} from 'react'
const MsgContext = createContext()
// 子组件
function Sub(){
return (
<MsgContext.Consumer>
{
({msg})=><h2>子组件:{msg}</h2>
}
</MsgContext.Consumer>
)
}
// 父组件
function Father(){
return <Sub />
}
// 顶级组件
export default function App(){
return (
<MsgContext.Provider value={{msg: "你好世界"}}>
<Father />
</MsgContext.Provider>
)
}
6.3.2 useContext--上下文空间创建后,子组件也可以使用useContext这个Hook调用上下文空间。
import React, {createContext, useContext} from 'react'
const MsgContext = createContext()
// 子组件
function Sub(){
const {msg} = useContext(MsgContext); // 使用useContext调用上下文空间
return <h2>子组件:{msg}</h2>
}
// 父组件
function Father(){
return <Sub />
}
// 顶级组件
export default function App(){
return (
<MsgContext.Provider value={{msg: "你好世界"}}>
<Father />
</MsgContext.Provider>
)
}
6.4 useRef--获取某个组件或元素
//1、不受控组件代表表单元素的值不受state控制,那么获取表单元素的值便只能空过ref获取(函数式组件中使用useRef获取)。
import React, {useRef} from 'react'
export default function App(){
const element = useRef(null);
return (
<>
<input type="text" ref={element} />
<button onClick={()=>console.log(element.current.value)}>获取input的值</button>
</>
)
}
//2、受控组件代表表单元素的值受state控制(函数式组件中可以理解为受useState控制),获取其值时直接通过state获取即可:
import React, {useState} from 'react'
export default function App(){
const [msg, setMsg] = useState("");
return (
<>
<input type="text" value={msg} onChange={(e)=>setMsg(e.target.value)} />
<button onClick={()=>console.log(msg)}>获取input的值</button>
</>
)
}
6.5 memo
实际开发中,我们会意识到一个现象:当父组件更新时,子组件会被迫更新。 这就导致性能损耗,来看以下代码:
import React, {useState} from 'react'
// 子组件
function Sub(){
console.log('子组件被更新了')
return <div>子组件</div>
}
// 父组件
export default function App(){
const [msg, setMsg] = useState("你好世界");
return (
<>
<h2>内容为:{msg}</h2>
<button onClick={()=>setMsg("Hello World")}>修改Msg</button>
<hr />
<Sub />
</>
)
}
//此时,我们需要借助memo这个hook来缓存Sub组件,避免它被强制更新:
import React, {useState, memo} from 'react'
// 子组件
const Sub = memo(() => {
console.log('子组件被更新了')
return <div>子组件</div>
})
// 父组件
export default function App(){
const [msg, setMsg] = useState("你好世界");
return (
<>
<h2>内容为:{msg}</h2>
<button onClick={()=>setMsg("Hello World")}>修改Msg</button>
<hr />
<Sub />
</>
)
}
6.6 useMemo与useCallback
6.6.1 useCallback
//当把触发msg修改的按钮放到子组件后,msg一修改又会触发Sub的更新:
import React, { useState, memo } from 'react'
// 子组件
const Sub = memo((props) => {
console.log('子组件被更新了')
return <button onClick={props.mySetMsg}>修改Msg</button>
})
// 父组件
export default function App() {
const [msg, setMsg] = useState("你好世界");
const mySetMsg = () => setMsg("Hello World")
return (
<>
<h2>内容为:{msg}</h2>
<Sub mySetMsg={mySetMsg} />
</>
)
}
//此时,只要给mySetMsg方法套上useCallback即可:
const mySetMsg = useCallback(() => setMsg(()=>"Hello World"), []) // 此处setMsg()中需要使用callback形式
6.6.2 useMemo
//useMemo与useCallback差不多,只是useMemo需要在回调函数中再返回一个函数,我们称之为高阶函数:
const mySetMsg = useMemo(()=>{
return () => setMsg("Hello World")
}, [])