react 初级基础

react基本使用

项目创建

  • 项目的创建命令 npx create-react-app react-basic
  • 创建一个基本元素进行渲染 
// 1 导入react 和 react-dom
import React from "react";
import ReactDOM from "react-dom";

// 2 创建 react 元素 React提供了创建元素的api ReactDOM 渲染页面
/**
 * 第一个参数:要创建的标签名称
 * 第二个参数:元素自身的属性,没有就写null,有就传入一个对象
 * 第三个及以后的所有参数:统统为元素的子节点
 */
const h1 = React.createElement("a", { href: "http://baidu.com" }, "点击去百度");

// 3 渲染 react 元素 ReactDOM提供了渲染元素的api
ReactDOM.render(h1, document.querySelector("#root"));

注意:ReactDOM 渲染在React 18中不再被支持。请使用createRoot。在你切换到新的API之前,你的应用程序会像运行React 17一样运行

import { createRoot } from 'react-dom/client';
import App from './App'
const h1 = React.createElement("div", { id:'box',className:'demo' }, "hello");

const el = React.createElement("ul", { className: 'list' }, 
    React.createElement('li', null, '香蕉'),
    React.createElement('li',null,'香蕉'),
    React.createElement('li',null,'香蕉'),
);

const container = document.getElementById('root');
const root = createRoot(container);
root.render(h1)
root.render(el)
root.render(<App></App>)

JSX语法

jsx是react的核心内容

注意:JSX 不是标准的 JS 语法,是 JS 的语法扩展。脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法。

babel 试一试

在 babel 的试一试中,可以通过切换 React Runtime 来查看:

  1. Classic -> React.createElement:注意这种方式,需要依赖于 React,也就是只要在代码中写 JSX 就必须导入 React 包

  2. Automatic -> _jsxRuntime:不依赖于 React 包,因此,代码中可以省略 React 包的导入【最新的脚手架已经默认值采用这种形式】

总结

  1. 使用jsx创建元素是react声明式UI的体现

  2. 使用jsx语法更加直观,降低学习成本,提高开发效率

jsx在 react 内部的运行做了什么转换?React Runtime(react运行时)

React Runtime(react运行时)分两种形式,一种是通过Classic转换成了传统的 eact.createElement 元素形式,另一种是通过Automatic ->_jsxRuntime.jsx的形式(可见babel官网)

项目配置工作:

  • 使用prettier插件格式化,可以格式化react代码(settings.json)

// 编辑器保存的时候用使用prettier进行格式化

"editor.formatOnSave": true,

// 编辑器默认使用prittier作为格式化工具

"editor.defaultFormatter": "esbenp.prettier-vscode",

// 不要有分号

"prettier.semi": false,

// 使用单引号

"prettier.singleQuote": true,

  •  配置vscode ,在vscode中使用tab键可以快速生成html内容进行标签提示(settings.json)
// 配置vscode ,在vscode中使用tab键可以快速生成html内容
// 在js中启用emmet语法
"emmet.includeLanguages": {
    "vue-html":"html",
    "javascript":"javascriptreact",
    "postcss":"css",
    "wxml":"html"
},
// 按tab键展开emmet语法
"emmet.triggerExpansionOnTab": true,
"emmet.showSuggestionsAsSnippets": true

jsx中渲染JS数据

jsx格式

  • jsx中可以出现任意的js表达式

  • 在jsx中只能出现js表达式,不能出现js语句,比如ifforwhile

const name = 'zs'
const age = 18

// 这就是jsx 表达格式
const title = (
  <h1>
    姓名:{name}, 年龄:{age}
  </h1>
)

// jsx 本身也可以是一个表达式
const span = <span>我是一个span</span>
const title = <h1>JSX 做为表达式:{span}</h1>
使用 单花括号,括号中可以使用js表达式,不能使用语句例如if
JS 表达式:数据类型和运算符的组合(可以单独出现数据类型,也可以数据类型+运算符的组合)
+ + 特点:==有值== 或者说 能够计算出一个值
  + 字符串、数值、布尔值、null、undefined、object( [] / {} )
  + 1 + 2、'abc'.split('')、['a', 'b'].join('-')
  + function fn() {}、 fn() - 注意:*函数调用可以渲染返回值,函数不能直接渲染,但是,
将来可以作为事件处理程序,因此,总的来说也可以出现在 {} 中*
  + 验证是不是 JS 表达式的技巧:看内容能不能作为方法的参数,比如,`console.log( 表达式 )`
  + jsx本身也是一个表达式

+ 在jsx中使用表达式语法:`{ JS 表达式 }`
  - 比如,`<h1>你好,我叫 {name}</h1>`
const name = '强哥'
const h1 = <ul>{ name}</ul>

 jsx条件渲染

  • 在react中,一切都是javascript,所以条件渲染完全是通过js来控制的

  • 在react中,可以使用if/else三元运算符逻辑与(&&)运算符实现条件的渲染

const isLoding = false
const loadData = () => {
  if (isLoding) {
    return <div>数据加载中.....</div>
  } else {
    return <div>数据加载完成,此处显示加载后的数据</div>
  }
}

const title = <div>条件渲染:{loadData()}</div>

jsx列表渲染

  • 作用:重复生成相同的 HTML 结构,比如,歌曲列表、商品列表等

  • 实现:使用数组的 map 方法

  • 注意:需要为遍历项添加 key 属性

    • key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用的

    • key 在当前列表中要唯一

    • 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值

    • 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值

const songs = [
  { id: 1, name: '痴心绝对' }, 
  { id: 2, name: '像我这样的人' }, 
  { id: 3, name: '南山南' }
]

const dv = (
  <div>
    <ul>{songs.map(song => <li key={song.id}>{song.name}</li>)}</ul>
  </div>
)

jsx样式处理

  • 行内样式 - style

    1. 像 width/height 等属性,可以省略 px,直接使用 数值 即可

    2. 如果是需要使用百分比的单位,此时,继续使用字符串的值即可(比如,"60%"

  • 类名 - className【推荐】实际项目中推荐使用classNames

  • 行内样式-style

const dv = (
  <div style={ { color: 'red', backgroundColor: 'pink' } }>style样式</div>
)
  • 类名-className

// 导入样式
import './base.css'
const dv = <div className="title">style样式</div>

总结:

组件基础

React创建组件-函数组件

  • 函数组件:使用 JS 的函数(或箭头函数)创建的组件,叫做函数组件

    • 约定1:函数名称必须以大写字母开头,React 据此区分组件和普通的 HTML标签

    • 约定2:函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null

  • 使用函数创建组件

// 使用普通函数创建组件:
function Hello() {
  return <div>这是我的第一个函数组件!</div>
}

// 使用箭头函数创建组件:
const Hello = () => <div>这是我的第一个函数组件!</div>
  •  使用组件

    • 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数返回值对应的内容

    • 使用函数名称作为组件标签名称

// 使用 单标签/双标签 渲染组件:
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
        <>
            <Button />,
            <Hello />
        </>
)

React创建组件-类组件

  • 使用 ES6 的 class 创建的组件,叫做类(class)组件

  • 约定1:类名称也必须以大写字母开头

  • 约定2:类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性

  • 约定3:类组件必须提供 render 方法

  • 约定4:render 方法必须有返回值,表示该组件的 UI 结构

import React, { Component} from "react";
import { createRoot } from 'react-dom/client';
// class Create extends React.Component{}
class Create extends Component{
    render(){
        return <span>React创建组件-类组件</span>
    }
}

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<>
    <Create></Create>
</>)

 将组件提取到单独的js文件中

  • 将react组件提取到独立的js文件中,组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中
// index.js
import Hello from './Hello'
// 渲染导入的Hello组件
root.render(<Create></Create>)


// Hello.js
import { Component } from 'react'
class Hello extends Component {
  render() {
    return <div>Hello Class Component!</div>
  }
}
// 导出Hello组件
export default Hello

有状态组件和无状态组件

  • 函数组件又叫做无状态组件 函数组件是不能自己提供数据【前提:不考虑 hooks 的情况下】,函数组件是没有状态的,只负责页面的展示态,不会发生变化),性能比较高
  • 类组件又叫做有状态组件 类组件可以自己提供数据,负责更新UI,只要类组件的数据发生了改变,UI 就会发生更新态)
  • 函数组件和类组件的区别:有没有状态【前提:不考虑 hooks 的情况下】,状态(state)是组件的私有数据,当组件的状态发生了改变,页面结构也就发生了改变(数据驱动视图),在项目中,一般都是由函数组件和类组件共同配合来完成的
  • 比如计数器案例,点击按钮让数值+1, 0和1就是不同时刻的状态,当状态从0变成1之后,UI也要跟着发生变化。React想要实现这种功能,就需要使用有状态组件来完成

  • 类组件的状态

    • 状态state,即数据,是组件内部的私有数据,只能在组件内部使用

    • 状态 state 的值是对象,表示一个组件中可以有多个数据

    • 通过 this.state.xxx 来获取状态

class Hello extends Component {
  // 为组件提供状态
  state = {
    count: 0,
    list: [],
    isLoading: true
  }

  render() {
   // 通过 this.state 来访问类组件的状态
    return (
      <div>计数器:{this.state.count}</div>
    )
  }
}

 setState 修改状态

  • react核心理念 -- 状态不可变 状态不可变指的是:不要直接修改状态的值,而是基于当前状态创建新的状态值
  • 语法:this.setState({ 要修改的部分数据 })
  • setState() 作用:1 修改 state 2 更新 UI,setState 是从 Component 父类继承过来的
  • 思想:数据驱动视图,也就是只需要修改数据(状态)那么页面(视图)就会自动刷新
class Hello extends Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState({
      count:  this.state.count + 10
    })
  }  

  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>按钮+1</button>
      </div>
    )
  }
}

setState 进阶

理解setState 延迟更新数据

  • setState 方法更新状态是同步的,但是表现为延迟更新状态(注意:非异步更新状态!!!

  • 延迟更新状态的说明:

    • 调用 setState 时,将要更新的状态对象,放到一个更新队列中暂存起来(没有立即更新)

    • 如果多次调用 setState 更新状态,状态会进行合并,后面覆盖前面

    • 等到所有的操作都执行完毕,React 会拿到最终的状态,然后触发组件更新

  • 优势:多次调用 setState() ,只会触发一次重新渲染,提升性能

setState箭头函数的语法

  • 解决多次调用依赖的问题

  • 推荐:使用 setState((prevState) => {}) 语法

  • 参数 prevState:表示上一次 setState 更新后的状态

this.setState((prevState) => {
  return {
    count: prevState.count + 1
  }
})

 使用setState的回调函数

  • 第二个参数,操作渲染后的DOM
  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
  • 语法:setState(updater[, callback])

this.setState(
    (state) => ({}),
    () => {console.log('这个回调函数会在状态更新后并且 DOM 更新后执行')}
)

同步or异步

  • setState本身并不是一个异步方法,其之所以会表现出一种“异步”的形式,是因为react框架本身的一个性能优化机制

  • React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新

  • setState如果是在react的生命周期中或者是事件处理函数中,表现出来为:延迟合并更新(“异步更新”)

  • setState如果是在setTimeout/setInterval或者原生事件中,表现出来是:立即更新(“同步更新”)

class App extends Component {
  state = {
    count: 0
  }
  // 点击按钮,分别查看 情况1 和 情况2 的 render 打印次数
  handleClick = () => {
    // 情况1
    // react框架内部有一个表示批量更新的标记isBatchingUpdates
    // 当我们点击按钮触发了react自己的事件后,这个标记就被修改为true,表示合并批量更新,
    // 所以,此时调用setState的时候,并不会立即更新状态,而是存储到了一个队列中
    // 将来等到所有的操作都执行完成后,Ract就会合并所有的状态更新,一次性的更新状态
    this.setState({
      count: this.state.count + 1
    })
    this.setState({
      count: this.state.count + 1
    })
    
    // 情况2
    // 注意:这种情况下,每次调用setState都会立即更新状态,并且让组件重新渲染
    // 因为定时器中的代码是异步代码,定时器中的代码不会被立即执行而是放到了事件队列
    // 事件队列中的代码,会在JS主线程(同步代码)都执行完成后,再执行。
    // 当主线程中的代码执行完成后,React已经将isBatchingUpdates标记设置为false
    // 此时,再调用setState(),此时,因为批量合并更新的标记已经是false
    // 所以,React会调用一次setState就立即更新一次状态,并且立即渲染
    // setTimeout(() => {
    //   this.setState({
    //     count: this.state.count + 1
    //   })
    //   this.setState({
    //     count: this.state.count + 1
    //   })
    // }, 0)

    // ReactDOM.unstable_batchedUpdates这个api可以解决下面这种叠加更新的问题
    // setTimeout(() => {
    //     ReactDOM.unstable_batchedUpdates(() => {
    //         this.setState({
    //             count: this.state.count + 1
    //         })
    //         this.setState({
    //             count: this.state.count + 1
    //         })
    //     })
    // }, 0)
  }

  render() {
    console.log('render')
    return (
      <div>
        <Child count={this.state.count} />
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

表单 -- 受控组件

  •  能够使用受控组件的方式获取文本框的值
  • HTML中表单元素是可输入的,即表单元素维护着自己的可变状态(value)

  • 在react中,可变状态通常是保存在state中的,并且要求状态只能通过setState进行修改

  • React中将state中的数据与表单元素的value值绑定到了一起,由state的值来控制表单元素的值

  • 受控组件:value值受到了react状态控制的表单元素

  • 使用受控组件的方式处理表单元素后,状态的值就是表单元素的值。即:想要操作表单元素的值,只需要操作对应的状态即可

class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleChange = (e) => {
    this.setState({
      msg: e.target.value
    })
  }

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

表单 -- 非受控组件-ref

  •  非受控组件借助于ref,使用原生DOM的方式来获取表单元素的值
// 1 导入 createRef 函数( 用来创建 ref 对象 )
import { createRef } from 'react'

class Hello extends Component {
  // 2 调用 createRef 函数来创建一个 ref 对象
  //   命名要规范: txt(DOM 元素的自己标识) + Ref
  txtRef = createRef()

  handleClick = () => {
    // 4 获取文本框的值:
    console.log(this.txtRef.current.value)
  }

  render() {
    return (
      <div>
         // 3 将创建好的 ref 对象,设置为 input 标签的 ref 属性值
        <input ref={this.txtRef} />
        <button onClick={this.handleClick}>获取文本框的值</button>
      </div>
    )
  }
}

事件处理

注册事件

  • React注册事件与DOM的事件语法非常像

  • 语法on+事件名 ={事件处理程序} 比如onClick={this.handleClick}

  • 注意:React事件采用驼峰命名法,比如onMouseEnter, onClick

- React注册事件与DOM的事件语法非常像

- 语法`on+事件名 ={事件处理程序}` 比如`onClick={this.handleClick}`

- 注意:*React事件采用驼峰命名法*,比如`onMouseEnter`, `onClick`

事件对象 

  • 可以通过事件处理程序的参数获取到事件对象

  • 注意:React 中的事件对象是 React 内部处理后的事件对象,一般称为:SyntheticBaseEvent 合成事件对象。用法与 DOM 原生的事件对象用法基本一致

function handleClick(e) {
  e.preventDefault()
  console.log('事件对象', e)
}

<a onClick={this.handleClick}>点我,不会跳转页面</a>

  this指向问题

  • 事件处理程序中的this指向的是undefined

  • render方法中的this指向的是当前react组件。只有事件处理程序中的this有问题

  • 原因:事件处理程序的函数式函数调用模式,在严格模式下,this指向undefined

  • render函数是被组件实例调用的,因此render函数中的this指向当前组件

下述代码:在react的事件处理函数中,this指向`undefined`
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg) // this是undefined
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

this指向解决方案 

  • 方式1:箭头函数
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={() => this.handleClick()}>点我</button>
      </div>
    )
  }
}
  •  方式2:bind,绑定this,返回一个新函数=>事件处理程序
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>点我</button>
      </div>
    )
  }
}
  •  方式3:箭头函数形式的实例方法 - 推荐使用
class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleClick = () => {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

组件通讯

组件是独立且封闭的单元,默认情况下只能使用组件自己的数据,在组件化过程中,通常会将一个完整的功能拆分成多个组件,用这种组件拆分去更好的完成整个应用的功能也会使其代码结构更加清晰化,而在这个过程中,多个组件之间不可避免的要共享某些数据 ,为了实现多个组件之间共享数据 ,就需要打破组件的独立封闭性,让其与外界沟通。这个共享数据的过程就是组件通讯

props基本使用

  • 如何传递?给组件标签添加属性,就表示给组件传递数据

<Common

names='ggc'

age={18}

style={{ color: 'red' }}

obj={{ li: '123' }}

list={[123, 456]}

list2={[{ age: 124, namei: 'io' }]}

jsx={<div>Jsx</div>}> // 还能传jsx 可以当插槽

</Common>

  •  如何接收?函数组件通过参数 props 接收数据,类组件通过 this.props

函数组件:

const Common = (props) => {

    console.log(props);

    return <>

        <div style={props.style}>

            <span>{props.names}</span><br />

            <span>{props.age}</span><br />

            <span>{props.obj.li}</span><br />

            <span>{props.list}</span><br />

            <span>{props.list2[0].age}</span><br />

            <span>{props.jsx}</span><br />

        </div>

    </>

}

类组件:

class Common extends Component {

        render(){

                console.log(this.props) // {names: 'ggc', age: 18}

        }

}

  • props的注意点

    •  props 是只读对象,也就是说:只能读取对象中的属性,无法修改
    • 单向数据流,也叫做:自顶而下(自上而下)的数据流
    • 可以传递任意数据(数字 字符串 布尔类型 数组 对象 函数 jsx)

组件通讯-父传子 

// 父组件 FatherCom
import { Component } from "react";
import ChildrenCom  from './children'
export default class FatherCom extends Component {
    state={
      money:100
    }
    render() {
        const {money} = this.state
        return <>
            <div>
                <div>
                     {/* 将数据传递给子组件 */}
                    <ChildrenCom money={money} />
                </div>
            </div>
        </>
    }
}
// 子组件 Childrens 
export const Childrens = (props) => {
    return <div>
        {/* 接收父组件中传递过来的数据 */}
        <h3>我是子组件 -- {props.money}</h3>
    </div>

}

// 子组件 ChildrenCom
export default class ChildrenCom extends Component{
    render() {
            return  <div>
              {/* 接收父组件中传递过来的数据 */}
    	      <h3>我是子组件 -- {this.props.money}</h3>
            </div>
    }
}

组件通讯-子传父

  • 利用回调函数来实现,父组件提供回调,子组件调用回调,将要传递的数据作为回调函数的参数

     步骤

  1. 父组件提供一个回调函数(用于接收数据)

  2. 将该函数作为属性的值,传递给子组件

  3. 子组件通过 props 调用回调函数

  4. 将子组件的数据作为参数传递给父组件的回调函数,父组件的函数通过setState方法修改数据

// 父组件 Father.js
import { Component } from "react";
import Childrens from './children'
export default class FatherCom extends Component {
    state = {
        money:16
    }
    changeMoney = (count) => {
        this.setState({
            money:this.state.money+count
        })
    }
    render() {
        const { money } = this.state
        return <>
            <div>
                <div>
                    {/* 将数据传递给子组件 */}
                    <Childrens money={money} changeMoney={ this.changeMoney} />
                </div>
            </div>
        </>
    }
}
Children.js

import { Component } from "react";
export default class ChildrenCom extends Component {
    handerClick = () => {
        this.props.changeMoney(50)
    }
    render() {
        return <div>
            {/* 接收父组件中传递过来的数据 */}
            <h3>我是子组件 -- {this.props.money}</h3>
            <button onClick={this.handerClick}>按钮+10</button>
        </div>
    }
}

组件通讯-兄弟组件

  • 思想:状态提升

  • 解释:将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态

  • 公共父组件职责:

    1. 提供共享状态

    2. 提供操作共享状态的方法

  • 要通讯的子组件只需通过 props 接收状态或操作状态的方法

index.js

import React, { Component } from 'react'
import ReactDOM from 'react-dom'

// 导入两个子组件
import Jack from './Jack'
import Rose from './Rose'

// App 是父组件
class App extends Component {
  // 1. 状态提升到父组件
  state = {
    msg: '',
  }

  changeMsg = msg => {
    this.setState({
      msg,
    })
  }
  
  render() {
    return (
      <div>
        <h1>我是App组件</h1>
        {/* 兄弟组件 1 */}
        <Jack say={this.changeMsg}></Jack>
				{/* 兄弟组件 2 */}
        <Rose msg={this.state.msg}></Rose>
      </div>
    )
  }
}

// 渲染组件
ReactDOM.render(<App />, document.getElementById('root'))

Jack.js

import React, { Component } from 'react'

export default class Jack extends Component {
  say = () => {
    // 修改数据
    this.props.say('you jump i look')
  }
  render() {
    return (
      <div>
        <h3>我是Jack组件</h3>
        <button onClick={this.say}>说</button>
      </div>
    )
  }
}

Rose.js

import React, { Component } from 'react'

export default class Rose extends Component {
  render() {
    return (
      <div>
        {/* 展示数据 */}
        <h3>我是Rose组件-{this.props.msg}</h3>
      </div>
    )
  }
}

 组件通讯-跨级通讯Context

  • 实现方式:使用 Context 来实现跨组件传递数据

  • Context 上下文,可以理解一个范围,只要在这个范围内容,就可以直接夸组件传递数据

// 0 导入创建 context 的函数
import { createContext } from 'react'

// 1 创建 Context 对象
//	 createContext 的返回值是一个 对象
//	 对象中包含了两个组件,分别是: Provider 状态的提供者组件(提供状态)  Consumer 状态的消费者组件(使用状态)
const { Provider, Consumer } = createContext()

// 2 使用 Provider 组件包裹整个应用,并通过 value 属性提供要共享的数据
<Provider value="blue">
  <div className="App">
    <Node /> 
  </div>
</Provider>

// 3 使用 Consumer 组件接收要共享的数据
<Consumer>
  {color => <span>data参数表示接收到的数据 -- {color}</span>}
</Consumer>

组件进阶用法

children属性

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

  • children 属性与普通的 props 一样,值可以是任意值(文本、React元素、组件,甚至是函数

  • 使用场景:做标题栏,或者当默认插槽使用

const Hello = props => {
  return (
    <div>
      该组件的子节点:{props.children}
    </div>
  )
}

<Hello>我是子节点</Hello>
// <Hello>我是子节点</Hello>
// <Hello><span style={{ color: 'red' }}>花哨的标题</span></Hello>
// <Hello>()=>{}</Hello>

props 校验

props 校验文档  

props校验-基本用法

允许在创建组件的时候,就约定props的格式、类型等

  1. 安装属性校验的包:yarn add prop-types

  2. 导入 prop-types

  3. 使用组件名.propTypes = {} 来给组件 List 的props添加校验规则

  4. 为组件添加 propTypes 属性,并通过 PropTypes 对象来指定属性的类型

函数组件-prop属性校验用法:

import PropTypes from 'prop-types'

const List = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
  return <ul>{lis}</ul>
}

List.propTypes = {
  colors: PropTypes.array
}

类组件-prop属性校验用法:

  • 第一种 用法和上面一样使用《组件.propTypes》

class List extends Component {
  render() {
    const arr = this.props.colors
    const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
    return <ul>{lis}</ul>
  }
}

List.propTypes = {
  colors: PropTypes.array
}

  • 第二种 用法组件内部使用-类的静态属性-static

class List extends Component {
  static propTypes = {
    colors: PropTypes.array,
    gender: PropTypes.oneOf(['male', 'female']).isRequired
  }

  static defaultProps = {
        gender: ''
  }
  
  render() {
    const arr = this.props.colors
    const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
    return <ul>{lis}</ul>
  }
}

扩展:类的静态属性-static:类的static语法简化props校验和默认值 ,静态属性是加给类自身的 

举例: Person 是类,p 是类的实例对象,实例的东西(属性方法)是加给实例的需要new出来访问的,静态属性是加给类自身的 

class Person {
  // 实例属性
  name = 'zs'
    // 实例方法
  sayHi() {
    console.log('哈哈')
  }

  // 静态属性
  static age = 18
    // 静态方法
  static goodBye() {
    console.log('byebye')
  }
}
const p = new Person()

console.log(p.name)         // 访问实例属性
p.sayHi()                                // 调用实例方法

console.log(Person.age)    // 访问静态属性
Person.goodBye()                // 调用静态方法

props校验-规则

  1. 常见类型:array、bool、func、number、object、string

  2. React元素类型:element

  3. 必填项:isRequired

  4. 特定结构的对象:shape({})

// 常见类型
optionalFunc: PropTypes.func,
// 必选
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
})

props默认值

  • 通过defaultProps可以给组件的props设置默认值,在未传入props的时候生效,,对于函数组件来说,新版 React 已经不再推荐使用 defaultProps 来添加默认值,而是推荐使用函数参数的默认值来实现

// 通过函数参数默认值,来提供 props 默认值
const App = ({ pageSize = 10 }) {
  return (
    <div>
      此处展示props的默认值:{props.pageSize}
    </div>
  )
}

// 不传入pageSize属性
<App />

组件生命周期

  • 生命周期:一个事物从创建到最后消亡经历的整个过程

  • 注意:只有 类组件 才有生命周期

React生命周期方法图

组件-挂载阶段

  • 执行顺序: constructor=》render=》componentDidMount

组件的挂载阶段的钩子函数以及执行时机

钩子函数 触发时机 作用 constructor 创建组件时,最先执行 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 render 每次组件渲染都会触发 渲染UI( 注意: 不能调用setState() ) componentDidMount 组件挂载(完成DOM渲染)后 1. 发送网络请求 2.DOM操作

示例代码:

constructor 这个阶段的钩子作为了解,在新版更新中这种使用方法已经不被推荐
注意:对于class来说,如果继承了父类,并且在class中手动写了constructor, 那么,须手动调用super,super指的是:父类的constructor
注意:对于React组件来说,如果写了constructor,那么,应该将props传递给
super这样,才能保证在constructor中能够通过this.props来拿到props值

class Dadoudou extends Component {
    // constructor(props) {
    //     console.log('constructor---创建组件时1');
    //     super(props)
    //     this.state = {
    //         count: 0
    //     }
    //     this.txtRef = createRef()
    //     this.handleClick = this.handleClick.bind(this)
    // }

    // 初始化state、创建 Ref、使用 bind 解决 this 指向问题等更推荐下面这种用法
    state = {}
    txtRef = createRef()
    handleClick = () => { }

    componentDidMount() {
        // 发送网络请求,dom操作
        console.log('componentDidMount---组件挂载(完成DOM渲染)后3');
    }

    render() {
        // 不能调用setState()会造成死循环
        console.log('render---组件渲染2');
        return (
            <div>
                <h1>统计豆豆被打的次数:{this.state.count}</h1>
                <button>打豆豆</button>
            </div>
        )
    }
}

组件-更新阶段

执行顺序: render =》componentDidUpdate

组件的更新阶段的钩子函数以及执行时机

钩子函数 触发时机 作用 render 每次组件渲染都会触发 渲染UI(与 挂载阶段 是同一个render) componentDidUpdate 组件更新(完成DOM渲染)后 DOM操作,可以获取到更新后的DOM内容, 不要直接调用setState

执行时机:以下三者任意一种变化,组件就会重新渲染

  • setState()

  • forceUpdate() 强制组件更新

  • 组件接收到新的props(实际上,只需要父组件更新,子组件就会重新渲染)

注意:不要直接在 componentDidUpdate 钩子函数中调用setstate ( 这个意思是说:可以调用,但是,必须要放在某个条件判断中 不能直接调用,因为直接调用就会循环更新导致死循环)官方文档说明: React.Component – React

componentDidUpdate 钩子函数的应用,可以参考这个视频:TabBar菜单高亮Bug分析和修复

组件-卸载阶段

  • 执行时机:组件从页面中消失

    钩子函数触发时机作用
    componentWillUnmount组件卸载(从页面中消失)执行清理工作(比如:清理定时器等)

 示例代码:清理工作指的是你自己手动做的一些动作

import { Component } from 'react'

class Child extends Component {
    timer = ''
    handleResize() {
        console.log('window窗口改变了');
    }
    componentDidMount() {
        this.timer = setInterval(() => {
            console.log('我是定时器');
        }, 1000);
        window.addEventListener('resize', this.handleResize)
    }
    componentWillUnmount() {
        clearInterval(this.timer)
        window.removeEventListener('resize', this.handleResize)
    }
    render() {
        return <h1>统计豆豆被打的次数:</h1>
    }
}

export default class App extends Component {
    state = {
        count: 0
    }
    handleClick = () => {
        this.setState({
            count: this.state.count + 1
        })
    }
    render() {
        return (
            <div>
                {this.state.count === 0 ? <Child /> : <p>豆豆消失了</p>}
                <button onClick={this.handleClick}>打豆豆</button>
            </div>
        )
    }
}

json-server

  • 作用:根据 json 文件创建 RESTful 格式的 API 接口,模拟虚假数据

  • json-server采用的就是REST API的设计风格

REST API:说明

RESTFul API (或 REST API)是一套流行的接口设计风格,参考 RESTFul API 设计指南

使用 RESTFul API 时,常用请求方式有以下几种:

  • GET(查询):从服务器获取资源

  • POST(新建):在服务器新建一个资源

  • DELETE(删除):从服务器删除资源

  • PUT(更新):在服务器更新资源(注意:需要提供接口所需的完整数据)

  • PATCH(更新):在服务器更新资源(只需要提供接口中要修改过的数据)

约定:请求方式是动词,接口地址使用名词

 // PUT 和 PATCH 请求的区别演示:

比如,动物园数据包含:{ name, address, animals },假设要修改 name 属性,此时:
使用 patch 请求:只需要传入 { name } 即可
使用 put 请求:需要传入完成数据 { name, address, animals }

用法

  1. 准备文件data.json文件

{
  "tabs": [
    {
        "id": 1,
        "name": "热度",
        "type": "hot"
      },
      {
        "id": 2,
        "name": "时间",
        "type": "time"
      }
  ],
  "list": [
    {
      "id": 1,
      "author":"89 18k",
      "comment":"瓜子 啤酒",
      "time":"2021-11-24T03:13:40.644Z",
      "attitude":0
   	}
  ]
}

  2. 使用 json-server 启动接口

# 使用 npx 命令调用 json-server
# 指定用于提供数据的 json 文件
# --port 用于修改端口号
# --watch 表示监听 json 文件的变化,当变化时,可以不重启终端命令的前提下读取到json 文件中
的最新的内容
npx json-server ./data.json --port 8888 --watch

# 接口地址为:
http://localhost:8888/tabs
http://localhost:8888/list

3. 使用接口

axios.get('http://localhost:8888/tabs')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值