• React 框架


    1、React 框架简介

    1.1、介绍

    CS 与 BS结合:像 ReactVue 此类框架,转移了部分服务器的功能到客户端。将CSBS 加以结合。客户端只用请求一次服务器,服务器就将所有js代码返回给客户端,所有交互类操作都不再依赖服务器。 客户端只有在需要服务器的数据时才会使用json通信一下,其他时间都在客户端利用js操作、暂存数据这样就极大减轻了服务器压力。

    1.2、React 特性

    • 虚拟DOM树

    React 通过对 DOM 的模拟,最大限度地减少与DOM的交互。将网页的DOM树完全复制一份虚拟的 DOM 树放到内存中。

    •  数据驱动

    维护虚拟 DOM 树,当发现某些节点发生变化时,并不一定修改原来的 DOM 树(在网页中看到的每一个区域),比如某些分支可能会发生该改变时,首先会修改虚拟树中的这几个节点,而后将这几个节点与原节点对比。才会对真正发生改变的节点做出修改。

     1.3、JSX文件 

    为了构建交互式用户界面,使用了 JSXJSX 的完整版本是一个JavaScript语法扩展,它极大地简化了组件的创建。它支持HTML引用并使子组件渲染更容易。它本质上是一组 React 编写快捷方式。使用带有一些规则的 createElement 可以使源代码更具可读性和直接性。首先写jsx文件,运行时会先将所编写的jsx编译为js,编译完之后,将js文件运行在浏览器。

    2、配置环境

    2.1、安装 Git Bash

    Git bash 下载链接
    Git Bash 安装教程

    2.2、安装 Node.js

    Nodejs 下载链接
    Nodejs 安装教程

    2.3、安装 create-react-app

    打开 Git Bash,直接输入下面命令执行
    npm i -g create-react-app

    2.4、创建 React 项目,名为 React App

    在目标目录(自己存放项目的目录)下右键打开 Git Bash,执行下面的命令

    create-react-app react-app  # react-app可以替换为其它名称

    2.5、启动项目

    进入目录,目录进到 react-app 这一层,打开 Git Bash,输入下面命令

    npm start  # 启动应用

    3、组件(Component) 

    组件类似于一个 class,将一些 html、data(数据)、事件函数组合在一起成为一个组件

    3.1、定义组件

    定义好组件之后,需要将组件渲染出来,index.js 就是所有 js 的入口, 并引入React与Component组件。

    1. // box.js 文件
    2. import React, { Component } from 'react'; // 快捷键:imrc
    3. // 引入React原因:将jsx编译为js
    4. // 通过React.createElement()
    5. class Box extends Component { // 快捷键:CC
    6. state = { } //局部变量
    7. // component类的函数,用来返回当前组件最后的渲染html结构
    8. // render方法只能返回一个标签及所包含内容
    9. render() {
    10. return (
    11. <h1> Hello worldh1>
    12. );
    13. }
    14. }
    15. export default Box;
    1. // index.js 文件
    2. import React from 'react';
    3. import ReactDOM from 'react-dom/client';
    4. import './index.css';
    5. import 'bootstrap/dist/css/bootstrap.css'; // 引入bootstrap库
    6. import Box from './components/box'; // 引入box
    7. const root = ReactDOM.createRoot(document.getElementById('root'));
    8. root.render(
    9. <React.StrictMode>
    10. <Box /> // Box组件,见box.js
    11. React.StrictMode>
    12. );

    3.2、React Fragment 介绍与使用

    •  render()方法只能返回一个标签及所包含内容,想返回多个并列标签时需要包含在一个标签内。
    • React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让render()方法中返回多个元素。Fragments 允许将子列表分组,而无需向DOM添加额外标签。

    理解起来就是在我们定义组件的时候,return里最外层包裹的div往往不想渲染到页面,那么就要用到Fragment组件了。

    1. class Box extends Component {
    2. state = { }
    3. render() {
    4. return (
    5. <React.Fragment>
    6. <h1> Hello worldh1>
    7. <button>leftbutton>
    8. <button>rightbutton>
    9. React.Fragment>
    10. );
    11. }
    12. }

    3.3、在 jsx 中写 js 与 html 标签

    jsx 中可以在任意地方定义 html 标签,但要注意,jsx 里的 html 标签中写 js 代码时都需要加 {} 括起来,{} 中只能写表达式。

    1. render() {
    2. return (
    3. <React.Fragment>
    4. <h1>{this.toString()}h1> // html 标签内写js
    5. <button className="btn btn-primary">leftbutton>
    6. <button>rightbutton>
    7. React.Fragment>
    8. );
    9. }
    10. toString(){
    11. return `x: ${this.state.x}`;
    12. // 或者
    13. // const {x} = this.state; //ES6写法相当于const xxx = this.state.xxx
    14. // return `x:${x}`;
    15. }

    3.4、设置样式

    jsx文件下的html 标签中设置类名以用于 css 样式时,需要将 class =" "写为 className。 由于 jsx 下 html 标签与 js 语句混合,写 class 可能会与实际js中的同名类产生冲突。

      className

    1. return (
    2. <React.Fragment>
    3. <h1>{this.toString()}h1>
    4. <button className="btn btn-primary m-2">leftbutton>
    5. <button className="btn btn-success m-2">rightbutton>
    6. React.Fragment>
    7. // m-2 为 bootstrap 中 margin=2 的简写方式
    8. );

      style

    1. render() {
    2. return (
    3. <React.Fragment>
    4. // style样式:第一层{}代表里面是表达式,第二层{}代表里面是对象,即样式变量的内容
    5. <div style={{
    6. width: "50px",
    7. height: "50px",
    8. backgroundColor: "pink",
    9. color: "white",
    10. textAlign: "center",
    11. lineHeight: "50px",
    12. borderRadius: "5px",
    13. }}>{this.toString()}div>
    14. <button className="btn btn-primary m-2">leftbutton>
    15. <button className="btn btn-success m-2">rightbutton>
    16. React.Fragment>
    17. );
    18. }

    等价于:

    1. styles = {
    2. width: "50px",
    3. height: "50px",
    4. backgroundColor: "pink", // css中所有 - 命名均改为驼峰命名法
    5. }
    6. render() {
    7. return ( // 标签内 style={this.styele} 即可
    8. <React.Fragment>
    9. <div style={this.styles}>{this.toString()}div>
    10. <button className="btn btn-primary m-2">leftbutton>
    11. <button className="btn btn-success m-2">rightbutton>
    12. React.Fragment>
    13. );
    14. }

    3.5、数据驱动改变 style

    将 style 与 state(局部变量) 值相关联,通过改变 state 里的值来改变 style。当局部变量改变时,通过接口实现所有被这个值影响的组件都改变。

    1. state = {
    2. x: 1,
    3. }
    4. getStyles() {
    5. let styles = {
    6. width: "50px",
    7. height: "50px",
    8. backgroundColor: "pink",
    9. color: "white",
    10. textAlign: "center",
    11. lineHeight: "50px",
    12. borderRadius: "5px",
    13. margin: '5px',
    14. };
    15. if (this.state.x === 0){
    16. styles.backgroundColor = 'orange'; // 数据驱动改变style
    17. }
    18. return styles;
    19. }
    1. render() {
    2. return (
    3. // 直接调用 getStyles()函数
    4. <React.Fragment>
    5. <div style={this.getStyles()}>{this.toString()}div>
    6. <button className="btn btn-primary m-2">leftbutton>
    7. <button className="btn btn-success m-2">rightbutton>
    8. React.Fragment>
    9. );
    10. }

    3.6、渲染列表

    使用 map 函数

    遍历类写法需给每个标签元素定义唯一 key 属性,用来帮助React快速找到被修改的DOM元素

    1. class Box extends Component {
    2. state = {
    3. x: 1,
    4. colors: ['red','green','blue'], // 定义渲染列表,这里用来修改div元素内容
    5. }
    6. render() {
    7. return (
    8. <React.Fragment>
    9. {this.state.colors.map(color => (
    10. <div key={color}>{color}div> // 这里建立div并将内容赋值为上述列表
    11. ))}
    12. React.Fragment>
    13. );
    14. }
    15. }

    3.7、Conditional Rendering

    A && B && C ...:从前往后,返回第一个为 false 的表达式。( 如果全为 true,就返回最后一个 true 表达式)
    A || B || C ...:从前往后,返回第一个为 true 的表达式。(如果全为 false,就返回最后一个 false 表达式。)

    即利用逻辑表达式的短路原则:

    1. render() {
    2. return (
    3. <React.Fragment>
    4. {this.state.colors.length === 0 && <p> No colorsp>}
    5. // 即当 colors 有元素时无操作, 即当 colors 无元素时显示 No color
    6. {this.state.colors.map(color => (
    7. <div key={color}>{color}div>
    8. ))}
    9. React.Fragment>
    10. );
    11. }

    3.8、绑定事件

    1. class Box extends Component {
    2. handleClickLeft(){
    3. console.log("click left",this);
    4. }
    5. handleClickRight(){
    6. console.log("click right",this);
    7. }
    8. render() {
    9. //仅仅是绑定函数,而不是在渲染时就将返回值传过来,因此handleClickleft不加()
    10. return (
    11. <React.Fragment>
    12. <div style={this.getStyles()}>{this.toString()}div>
    13. <button onClick={this.handleClickLeft} className="btn btn-primary m-2">leftbutton>
    14. <button onClick={this.handleClickRight} className="btn btn-success m-2">rightbutton>
    15. React.Fragment>
    16. }
    17. }

    此时,输出的 this 不是 Box 类,而是 undifind。

    1. 如何使得方法里的 this 仍属于 Box 类:
    2. // 法一:箭头函数(推荐)
    3. // 法二: bind 函数
    1. handleClickLeft=()=>{ // 法一:箭头函数
    2. console.log("click left",this);
    3. }
    4. handleClickRight(){
    5. console.log("click right",this);
    6. }
    7. render() {
    8. return (
    9. <React.Fragment>
    10. <div style={this.getStyles()}>{this.toString()}div>
    11. <button onClick={this.handleClickLeft} className="btn btn-primary m-2">leftbutton>
    12. <button onClick={this.handleClickRight.bind(this)} className="btn btn-success m-2">rightbutton>
    13. React.Fragment> // 法二:bind函数
    14. );
    15. }

    3.9、修改 state 里的值

    1. 直接 this.state.x-- 不会影响到页面div的显示的x值,
    2. 如果想要让 state 里的 x 的修改影响到render函数的话, 必须用 setState() 函数 (通过重新调用 render 修改 div 里 x 值)
    1. class Box extends Component {
    2. state = {
    3. x: 1,
    4. }
    5. handleClickLeft = () => {
    6. this.setState({ // setState() 函数
    7. x: this.state.x - 1
    8. });
    9. }
    10. handleClickRight = () => {
    11. this.setState({ // setState() 函数
    12. x: this.state.x + 1
    13. });
    14. }
    15. render() {
    16. return (
    17. <React.Fragment>
    18. <div style={this.getStyles()}>{this.toString()}div>
    19. <button onClick={this.handleClickLeft} className="btn btn-primary m-2">leftbutton>
    20. <button onClick={this.handleClickRight} className="btn btn-success m-2">rightbutton>
    21. React.Fragment>
    22. );
    23. }
    24. }

    3.10、通过按钮修改css属性

    将 state 里的值赋值给某一样式的属性,通过按钮修改state 值从而修改 css 样式

    1. class Box extends Component {
    2. state = {
    3. x: 10, // state值
    4. }
    5. handleClickLeft = () => {
    6. this.setState({
    7. x: this.state.x - 10 // setState() 修改 state值, 重新调用 render() 函数
    8. });
    9. }
    10. handleClickRight = () => {
    11. this.setState({
    12. x: this.state.x + 10 // setState() 修改 state值,重新调用 render() 函数
    13. });
    14. }
    15. render() {
    16. return (
    17. <React.Fragment>
    18. <div style={this.getStyles()}>{this.toString()}div>
    19. <button onClick={this.handleClickLeft} className="btn btn-primary m-2">leftbutton>
    20. <button onClick={this.handleClickRight} className="btn btn-success m-2">rightbutton>
    21. React.Fragment>
    22. );
    23. }
    24. getStyles() {
    25. let styles = {
    26. width: "50px",
    27. height: "50px",
    28. backgroundColor: "pink",
    29. color: "white",
    30. textAlign: "center",
    31. lineHeight: "50px",
    32. borderRadius: "5px",
    33. margin: '5px',
    34. marginLeft: this.state.x, // state值赋值给 css 属性值
    35. };
    36. if (this.state.x === 0){
    37. styles.backgroundColor = 'orange';
    38. }
    39. return styles;
    40. }
    41. }

    3.11、给事件函数添加参数

    1. handleClickRight = (step) => {
    2. this.setState({
    3. x: this.state.x + step
    4. });
    5. }
    1. handleClickRightTmp = () => {
    2. return this.handleClickRight(50);
    3. }
    1. render() {
    2. return (
    3. <React.Fragment>
    4. <div style={this.getStyles()}>{this.toString()}div>
    5. <button onClick={this.handleClickLeft} className="btn btn-primary m-2">leftbutton>
    6. <button onClick={this.handleClickRightTmp} className="btn btn-success m-2">rightbutton>
    7. React.Fragment>
    8. );
    9. }

    将 handleClickRight() 函数写为箭头匿名函数后等价于: 

    1. render() {
    2. // 绑定了一个调用含参 函数 handleClickLeft =(step)=>{ } 的匿名函数
    3. return (
    4. <React.Fragment>
    5. <div style={this.getStyles()}>{this.toString()}div>
    6. <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">leftbutton>
    7. <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">rightbutton>
    8. React.Fragment>
    9. );
    10. }

    综上,Box组件的构建步骤:

    1、定义 state 变量,并使得数据驱动 style

    2、构造 handleClickLeft =(step)=>{ } 带参函数,利用 setState() 函数改变 state 值;调用setState()能够重新加载 render 函数,才可以对里面的 div 显示进行修改

    3、给按钮的单击事件onClick绑定所写函数。这里绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数,()=>this.handleClickRight(10)

    1. class Box extends Component {
    2. // 1. 定义 state,并使得数据驱动style
    3. state = {
    4. x: 10,
    5. }
    6. // 2. 通过 handleClickLeft =(step)=>{ } 带参函数 与 setState() 改变state值
    7. // 并能够重新加载 render 函数来对里面的 div 显示进行操作
    8. handleClickLeft = (step) => {
    9. this.setState({
    10. x: this.state.x - step
    11. });
    12. }
    13. handleClickRight = (step) => {
    14. this.setState({
    15. x: this.state.x + step
    16. });
    17. }
    18. render() {
    19. // 3. 给事件绑定函数:通过 render 函数里,按钮事件绑定函数。
    20. // 绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数
    21. return (
    22. <React.Fragment>
    23. <div style={this.getStyles()}>{this.toString()}div>
    24. <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">leftbutton>
    25. <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">rightbutton>
    26. React.Fragment>
    27. );
    28. }
    29. getStyles() {
    30. let styles = {
    31. width: "50px",
    32. height: "50px",
    33. backgroundColor: "pink",
    34. color: "white",
    35. textAlign: "center",
    36. lineHeight: "50px",
    37. borderRadius: "5px",
    38. margin: '5px',
    39. marginLeft: this.state.x, // 数据驱动 style
    40. };
    41. if (this.state.x === 0){
    42. styles.backgroundColor = 'orange';
    43. }
    44. return styles;
    45. }
    46. }

    4、Component 组件的组合与交互

    4.1、【组合 Component 】组件的构建

    1. 组合多个上述定义的 Box 组件,形成 Boxes 组件,并完成 属性 值的传递。
    2. <注:多个相同子组件时,每个子组件需要有唯一 key 值>
    • 建立 Boxes 类组件,内含多个 Box组件
    1. import React, { Component } from 'react';
    2. import Box from './box';
    3. class Boxes extends Component {
    4. // 1. 设置 state 变量,包括 Box 组件的唯一 key 值与 x 坐标值。
    5. state = {
    6. boxes:[
    7. {id: 1, x: 10},
    8. {id: 2, x: 10},
    9. {id: 3, x: 100},
    10. {id: 4, x: 10},
    11. {id: 5, x: 10},
    12. ]
    13. }
    14. // 2. render 函数返回多个 box 组件,通过 map 列表,逐一建立并赋值多个 Box 组件
    15. // 将 box.id 赋值给组件唯一 key,将 box.x 赋值给 Box 组件的 x
    16. render() {
    17. return (
    18. <React.Fragment>
    19. {this.state.boxes.map((box)=>(
    20. <Box
    21. key = {box.id} // id
    22. x = {box.x} // 这里会自动找到 Box 组件里的 x 赋值并存储在 props
    23. // 但仅仅是修改了x并不会改变前端的显示
    24. />
    25. ))}
    26. React.Fragment>
    27. );
    28. }
    29. }
    30. export default Boxes;

    【注】在react组件之间的通信是通过props属性来完成的,比如父组件需要将数据传递给子组件,那么组件在渲染子组件的时候,直接将数据作为子组件的属性传参。

    •  state 值传递:通过 props 将 Boxes定义的属性值返回传递给 Box
    1. class Box extends Component {
    2. state = {
    3. // props类似于state,存储除key以外属于 box 的所有属性
    4. // Boxes 建立的 Box 赋值的 x 存到了 props 里
    5. // 通过 props 传递给了每个 Box
    6. x: this.props.x,
    7. }
    8. handleClickLeft = (step) => {
    9. this.setState({
    10. x: this.state.x - step
    11. });
    12. }
    13. handleClickRight = (step) => {
    14. this.setState({
    15. x: this.state.x + step
    16. });
    17. }
    18. render() {
    19. return (
    20. <React.Fragment>
    21. <div style={this.getStyles()}>{this.toString()}div>
    22. <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">leftbutton>
    23. <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">rightbutton>
    24. React.Fragment>
    25. );
    26. }
    27. getStyles() {
    28. let styles = {
    29. width: "50px",
    30. height: "50px",
    31. backgroundColor: "pink",
    32. color: "white",
    33. textAlign: "center",
    34. lineHeight: "50px",
    35. borderRadius: "5px",
    36. margin: '5px',
    37. marginLeft: this.state.x,
    38. };
    39. if (this.state.x === 0){
    40. styles.backgroundColor = 'orange';
    41. }
    42. return styles;
    43. }
    44. toString(){
    45. return `x: ${this.state.x}`;
    46. }
    47. }
    48. export default Box;
    • 标签传递:通过 props 将Boxes 增加的Box.children子标签,传递给 Box
    1. // Boxes.jsx文件
    2. render() {
    3. return (
    4. <React.Fragment>
    5. {this.state.boxes.map((box)=>(
    6. <Box key = {box.id} x = {box.x}>
    7. <p>Box :p> // 将 Box 的闭合标签写为双标签
    8. <div>{box.id}div> // 可在 Box 标签内增加其它标签,属性名为 Box.children
    9. Box> // 并存储到了 props 中
    10. ))}
    11. React.Fragment>
    12. );
    13. }
    1. // Box.jsx 文件
    2. render() {
    3. console.log(this.props);
    4. return (
    5. <React.Fragment>
    6. {this.props.children[0]} // 通过 props 所存储的 children 将增加的标签传递给Box
    7. <div style={this.getStyles()}>{this.toString()}div>
    8. <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">leftbutton>
    9. <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">rightbutton>
    10. React.Fragment>
    11. );
    12. }
    • 方法传递1:React 子组件调用父组件的方法。

    Box子组件调用Boxes父组件内的方法,依旧通过props。实现在 Box 组件中触发 onClick 事件后,在Boxes组件中删除key值对应的Box,即在Box标签内调用Boxes标签的方法

    1. // Boxes.jsx 文件
    2. // 1. Boxes.jsx 文件中写删除的方法
    3. handleDelete = (id) => {
    4. // filter: boxes列表的元素依次判断,若表达式为true则留下,否则删掉
    5. // 即若id不等留下来,相等删除
    6. const newboxes = this.state.boxes.filter(
    7. (x)=>(x.id !== id)
    8. );
    9. this.setState({
    10. boxes: newboxes
    11. })
    12. }
    13. render() {
    14. if(this.state.boxes.length === 0){
    15. return <div className='alert alert-dark'>没有元素可以删除了!!!div>
    16. }
    17. return (
    18. // 2. 将所写删除方法定义为标签的 onDelete 属性传递给 Box(会存储在 props中)
    19. <React.Fragment>
    20. {this.state.boxes.map((box)=>(
    21. <Box key = {box.id} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
    22. <p>Box :p>
    23. <div>{box.id}div>
    24. Box>
    25. ))}
    26. React.Fragment>
    27. );
    28. }
    1. // Box.jsx 文件
    2. render() {
    3. return (
    4. <React.Fragment>
    5. {this.props.children[0]}
    6. <div style={this.getStyles()}>{this.toString()}div>
    7. <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">leftbutton>
    8. <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">rightbutton>
    9. // 3. Box 调用 Boxes 的删除方法 :
    10. // Box 中的 Button 的 onClick 事件,绑定匿名函数来调用含参的删除方法
    11. <button onClick={()=>this.props.onDelete(this.props.id)} className='btn btn-danger m-2'> Delete button>
    12. React.Fragment>
    13. );
    14. }
    • 方法传递2:React 父组件调用子组件的方法。

    仅能调用一个子组件方法,无法调用列表子组件

    1. // 父组件
    2. class Boxes extends Component {
    3. // 1. Boxes 父组件中写入
    4. setChildRef = (ref) => {
    5. this.ChildRef = ref;
    6. }
    7. // 3. Boxes 父组件中写调用 Box 子组件的方法
    8. handleReset = () =>{
    9. this.ChildRef.handleRE()
    10. }
    11. render() {
    12. return (
    13. <React.Fragment>
    14. // 4. 将父组件方法绑定onClick单击事件中,即可实现单击调用子组件的方法
    15. <button onClick={this.handleReset} className='btn btn-primary'> Clear button>
    16. {this.state.boxes.map((box)=>(
    17. // 2. Boxes 父组件的 Box 子组件标签内增加 ref 属性,并将 setChildRef 传递过来
    18. <Box key = {box.id} ref={this.setChildRef} id={box.id} x = {box.x} onDelete = {this.handleDelete}>
    19. <p>Box :p>
    20. <div>{box.id}div>
    21. Box>
    22. ))}
    23. React.Fragment>
    24. );
    25. }
    26. }
    1. // 子组件
    2. class Box extends Component {
    3. state = {
    4. x: this.props.x,
    5. }
    6. // 子组件中被调用的方法
    7. handleRE = () =>{
    8. this.setState({
    9. x: 0
    10. });
    11. }
    12. render() {
    13. return (
    14. <React.Fragment>
    15. ......
    16. React.Fragment>
    17. );
    18. }
    19. }

    5、组件使用

    5.1、组件的生命周期

    创建时(挂载阶段) 

    • 执行时机:组件创建时(页面加载时)
    • 执行顺序:constructor() -> render() -> componentDidMount()

    constructor()组件创建的时候,最先执行,主要是初始化数据,为事件处理程序绑定this

    render():每次组件渲染触发,主要是渲染UI

    componentDidMOunt():组件挂载完成,主要作用DOM操作,发送网络请求

    更新时(更新阶段)

    • 执行时机:1.setState() 2.组件收到props变了 3.forceUpdate()
    • 说明:以上3中情况都会触发
    • 执行顺序:render() -> componentDidUpdate()

    render():每次组件渲染触发,渲染UI

    componentDidUpdate():组件 状态更新完毕

    预载时

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

     5.2、动态更新initialValue的值

    [antd: Form.Item] defaultValue will not work on controlled Field. You should use initialValues o
    原因
    :antd禁止在form.item下的组件使用默认属性
    解决办法:删除defaultValue,在中使用initialValue={{ parentId: parentId }}代替,如果要动态更新 parentId的值,又会导致下面的问题

    React does not recognize the initialValue prop on a DOM element.
    **原因:**不能通过这种方法更新parentId的值,想要更新props中的值,要通过form.setFieldsValue({ parentId: parentId })进行更改,

    你不能用控件的 value 或 defaultValue 等属性来设置表单域的值,默认值可以用 Form 里的 initialValues 来设置。注意 initialValues 不能被 setState 动态更新,你需要用 setFieldsValue 来更新。

    由上可知,表单中不能设置默认值,要想设置默认值必须通过form中的initial Values进行设置,但是其又不能动态更新,想要达到动态更新的效果必须使用form中的setFieldsValue进行更新,需要检测parentId的变化从而对parentId进行修改,固需要在useEffect中使用setFieldsValue

    如果需要在父组件中得到子组件的form表单中的值,可以通过函数传参的方式,将子组件的form对象传递给父组件,这样父组件得到子组件的form对象,就可以通过getFieldValue得到表单的值了。

    1. const [form] = Form.useForm()
    2. //将子组件的form传递给父组件,使得父组件可以取到子组件form中的categorieName的值
    3. props.setForm(form)
    4. // 动态更新parentId的值
    5. useEffect(() => {
    6. form.setFieldsValue({ parentId: parentId })
    7. }, [parentId])
    1. import React, { Fragment, useEffect } from 'react'
    2. import { Form, Input, Select } from 'antd'
    3. import { useForm } from 'antd/lib/form/Form';
    4. const { Option } = Select;
    5. export default function AddForm(props) {
    6. const { categorys, parentId } = props
    7. const [form] = Form.useForm()
    8. console.log(parentId)
    9. // 动态更新parentId的值
    10. useEffect(() => {
    11. form.setFieldsValue({ parentId: parentId })
    12. }, [parentId])
    13. return (
    14. <Fragment>
    15. {/* initialValues不能被动态更新,需要使用form.setFieldsValues进行动态更新,将其放在useEffect中 */}
    16. <Form form={form}>
    17. <span>所属分类span>
    18. <Form.Item
    19. name="parentId"
    20. rules={[
    21. {
    22. required: true,
    23. message: 'Please select category!',
    24. },
    25. ]}
    26. >
    27. {/* 表单中不能使用默认属性defaultValue,在form中使用initialValues{{}}代替 */}
    28. <Select >
    29. <Option value='0'>一级分类Option>
    30. {
    31. categorys.map(c => <Option key={c._id} value={c._id}>{c.name}Option>)
    32. }
    33. Select>
    34. Form.Item>
    35. <span>分类名称span>
    36. <Form.Item
    37. name="categoryName"
    38. rules={[
    39. {
    40. required: true,
    41. message: 'Please input your categoryName!'
    42. }
    43. ]}
    44. >
    45. <Input placeholder="添加分类" />
    46. Form.Item>
    47. Form>
    48. )
    49. }
    1. // 告警标签
    2. // 动态更新initialValue值
    3. changeMissLevel = () =>{
    4. const{form} = this.props;
    5. form.setFieldsValue(
    6. {
    7. missLevel:'222'
    8. }
    9. );
    10. }
    11. render(){
    12. <Form.Item>
    13. {getFieldDecorator('missLevel',{
    14. initialValue:this.state.missLevel
    15. })(
    16. <Select
    17. onChange={this.changeMissLevel}
    18. >
    19. <Option value= '1'>aOption>
    20. <Option value= '2'>bOption>
    21. Select>
    22. )
    23. }
    24. }

    5.3、遍历、转换、非空判断、动态设置

    • 遍历对象与数组
    1. // 遍历对象
    2. const object = {"a":"1","b":"2"};
    3. for(let key in object){
    4. console.log(key);
    5. console.log(object[key]);
    6. }
    7. // 遍历数组
    8. const array = ["1","2","3"];
    9. const array1 = [{"a":"1"},{"b":"2"}];
    10. for(let key in array){
    11. console.log(array[key]);
    12. }
    13. for(let key in array1){
    14. console.log(array[key].a);
    15. console.log(array[key].b);
    16. }
    •   对象动态值获取或作为键 
    1. // 动态值作为键
    2. const obj = {"a":"1","b":"2"};
    3. let ccc = 'c';
    4. obj[ccc] = '3';
    5. // 对象赋值
    6. obj.d = '4';
    7. // 对象获取值
    8. let ddd = obj.d;
    9. // 对象动态值获取
    10. const data = {"a":"1","b":"2"};
    11. let aaa = 'a';
    12. data[aaa+'']
    • 对象与字符串的转换 
    1. // 对象转字符串
    2. const obj = {"a":"1"}
    3. JSON.String(obj );
    4. // 字符串转对象
    5. String a = "";
    6. JSON.parse(a);
    7. // join与split
    8. const aa = ["1","2"];
    9. const bb = "1,2";
    10. bb = aa.join(',');
    11. aa = bb.split(",");
    • 非空判断 
    1. // 判断是否不为空
    2. ifNotNull = (value:any) => {
    3. if( value != undefined &&
    4. value != null &&
    5. value != 'null' &&
    6. value != 'NULL' &&
    7. value != '[]' &&
    8. value != '' &&
    9. value != {} &&
    10. value != [] &&
    11. )
    12. {
    13. return true;
    14. } else {
    15. return false;
    16. }
    17. }
    18. // 判断是否为空
    19. ifNotNull = (value:any) => {
    20. if( value === undefined ||
    21. value === null ||
    22. value === 'null' ||
    23. value === 'NULL' ||
    24. value === '[]' ||
    25. value === '' ||
    26. value === {} ||
    27. value === [] ||
    28. )
    29. {
    30. return true;
    31. } else {
    32. return false;
    33. }
    34. }

    5.4、ant的Search组件,搜索框内容清空 

    1. <Search
    2. ref= "searchBar"
    3. placeholder="请输入开关名称或开关描述"
    4. enterButton="搜索"
    5. size="large"
    6. onSearch={this.inputSearch}
    7. allowClear
    8. />
    9. // 清空操作
    10. reset = () => {
    11. this.refs.searchBar.input.state.value='';
    12. }

    5.5、资源树结构选中取消 

    1. onTreeSelect = (selectKeys:any[], info: { props: {dataRef: any}}}) => {
    2. // 树节点取消触发
    3. if(selectKeys[0] === undefined){
    4. this.setState({
    5. data:[],
    6. total:0
    7. });
    8. return;
    9. }
    10. // 选中
    11. let restypeId = info.node.props.dataRef.restypeId;
    12. }

    6、Hooks

    6.1、useState

    import { useState } from 'react';
    
    • 使用 useState 的注意事项 
    1. useState 向组件引入新的状态,这个状态会被保留在 react 中。
    2. useState 只有一个参数,这个参数是初始状态值,它可以是任意类型。
    3. useState 可以多次调用,意味着可以为组件传入多个状态。
    4. useState 返回值是一个数组,第一个元素就是我们定义的 state,第二个元素就是修改这个 state 的方法。接收 useState 的返回值使用数组结构语法,我们可以随意为 state 起名字。修改 state 的方法必须是 set + 状态名首字母大写构成,不按照约定写就会报错。
    5. useState 的参数也可以是一个函数,这个函数的返回值就是我们的初始状态。
    1. function Counter() {
    2. const [count, setCount] = useState(0);
    3. return (
    4. <div>
    5. <h3>{count}h3>
    6. <button onClick={() => setCount(count + 1)}>+button>
    7. div>
    8. );
    9. }

    定义了一个状态 count,传入的参数为函数 () => 1,则 count 的初始值为函数的返回值1。当点击按钮时,调用修改方法 setCount 将 count 的值自增1,页面上计数器也会同步自增1。

    1. function Counter() {
    2. const [count, setCount] = useState(() => 1);
    3. function handleCount(a) {
    4. setCount(a + 1);
    5. document.title = a;
    6. }
    7. return (
    8. <div>
    9. <h3>{count}h3>
    10. <button onClick={() => handleCount(count)}>+button>
    11. div>
    12. );
    13. }

    在点击按钮后,计数器显示数字变为了2,但页面的标题还是1。每次点击按钮后,页面的标题都比计数器显示数字少1。因此执行代码先执行了第5行的设置页面标题,才执行第4行的设置 count 值,看过上篇文章就很容易理解。

    6.2、useEffect

    import { useEffect } from 'react';

    useEffect 这个 Hook 函数的主要作用就是将副作用代码添加到函数组件内。所谓的副作用代码就是 dom 更改、计时器、数据请求等。

    使用场景:

    • useEffect(() => {}) 这种写法代表两个生命周期函数 componentDidMount 和 componentDidUpdate。
    • useEffect(() => {}, []) 这种写法代表生命周期函数 componentDidMount。
    • useEffect(() => () => {}) 这种写法代表组件卸载之前 componentWillUnmount 和 componentDidUpdate 两个生命周期函数。
    6.2.1、useEffect(() => {})

    useEffect 钩子函数传入一个函数作为参数,代表组件在加载完成后和数据更新时都会调用传入的函数。

    1. function Counter() {
    2. const [count, setCount] = useState(() => 1);
    3. function handleCount(a) {
    4. setCount(a + 1);
    5. document.title = a;
    6. }
    7. useEffect(() => {
    8. console.log('success');
    9. });
    10. return (
    11. <div>
    12. <h3>{count}h3>
    13. <button onClick={() => handleCount(count)}>+button>
    14. div>
    15. );
    16. }

    在页面加载完成和每次点击按钮让计数器自增后,控制台都会打印 success。即 useEffect Hook 实现了生命周期函数 componentDidMount 和 componentDidUpdate 的功能。 

    6.2.2、useEffect(() => {}, [])

    useEffect 钩子函数传入一个函数和一个数组作为参数,代表组件在加载完成后会调用传入的函数。

    1. function Counter() {
    2. const [count, setCount] = useState(() => 1);
    3. function handleCount(a) {
    4. setCount(a + 1);
    5. document.title = a;
    6. }
    7. useEffect(() => {
    8. console.log('success');
    9. }, []);
    10. return (
    11. <div>
    12. <h3>{count}h3>
    13. <button onClick={() => handleCount(count)}>+button>
    14. div>
    15. );
    16. }

    这个案例和上一个案例不同之处在于 useEffect 钩子函数传入了一个空数组作为第2个参数,当页面加载完成时控制台会打印 success,点击按钮更新计数器后不再打印,即实现了生命周期函数 componentDidMount 的功能。 

    6.2.3、useEffect(() => () => {})

    useEffect 钩子函数传入一个返回函数的函数作为参数,代表组件在数据更新时和卸载时会调用返回的函数。

    1. function Counter(props) {
    2. const [count, setCount] = useState(() => 1);
    3. function handleCount(a) {
    4. setCount(a + 1);
    5. document.title = a;
    6. }
    7. useEffect(() => () => {
    8. console.log('success');
    9. }, []);
    10. return (
    11. <div>
    12. <h3>{count}h3>
    13. <button onClick={() => handleCount(count)}>+button>
    14. <button onClick={() => props.root.unmount()}>卸载组件button>
    15. div>
    16. );
    17. }

    这个案例中,点击按钮更新计数器和点击卸载组件时控制台都会打印 success,即实现了生命周期函数 componentWillUnmount 和 componentDidUpdate 的功能。 

    6.2.4、useEffect 第二个参数的使用

    useEffect 钩子函数的第二个参数,正常添加空数组,代表的生命周期是 componentDidMount。即使我们修改了 state,useEffect 也只会调用一次。

    如果我们想让某个 state 发生改变的时候,继续调用 useEffect,就需要把这个状态添加到第二个参数的数组中。
     

    1. function Counter() {
    2. const [count, setCount] = useState(() => 1);
    3. const [person, setPerson] = useState({ name: 'ccshen' });
    4. function handleCount(a) {
    5. setCount(a + 1);
    6. document.title = a;
    7. }
    8. useEffect(() => {
    9. console.log('计数器改变');
    10. }, [count]); // count 一旦发生改变,就会执行 useEffect
    11. return (
    12. <div>
    13. <h3>{count}h3>
    14. <h3>{person.name}h3>
    15. <button onClick={() => handleCount(count)}>+button>
    16. <button onClick={() => setPerson({ name: 'lisi' })}>更改personbutton>
    17. div>
    18. );
    19. }

    useEffect 第二个参数传递了 count,那么将会在 count 状态更新时才会执行传入的函数。

    6.2.5、useEffect 的异步处理 
    1. function Counter() {
    2. function asyncFn() {
    3. setTimeout(() => {
    4. console.log('success');
    5. }, 1000);
    6. }
    7. useEffect(() => {
    8. asyncFn();
    9. });
    10. return (
    11. <div>
    12. div>
    13. );
    14. }

    我们声明了一个异步函数,然后在 useEffect 中调用,预览正常,在页面加载完成1秒后打印 success。 

    当我们使用 async/await 时

    1. function Counter() {
    2. function asyncFn() {
    3. setTimeout(() => {
    4. console.log('hahaha');
    5. }, 1000);
    6. }
    7. useEffect(async () => {
    8. await asyncFn();
    9. });
    10. return (
    11. <div>
    12. div>
    13. );
    14. }

    在 useEffect 中如果使用了异步函数,那就需要定义一个自调用函数 

    1. function Counter() {
    2. function asyncFn() {
    3. setTimeout(() => {
    4. console.log('hahaha');
    5. }, 1000);
    6. }
    7. useEffect(() => {
    8. (async () => {
    9. await asyncFn();
    10. })();
    11. });
    12. return (
    13. <div>
    14. div>
    15. );
    16. }

    这时控制台就不报警告了。

    遇到异步函数,我们需要在 useEffect 中添加一个自调用函数。

    6.3、useContext

    useContext 用于父组件向子孙组件传递数据,不需要再使用通过 props 从父组件向子组件逐级传递数据。如果组件多层嵌套,使用 props 来传值显得极其复杂,这时就需要使用 useContext。

    import { useContext, createContext } from 'react';

    首先定义一个 context 变量,用于存放当前上下文对象。将上下文对象的 Provider 作为父组件,通过 value 属性将要传递的值传给子孙组件。在子孙组件中就可以通过 useContext 获取到要传递的值。

    1. const myContext = createContext(); // 当前上下文对象
    2. function App() {
    3. const value = useContext(myContext);
    4. return (
    5. <div>{value}div>
    6. );
    7. }
    8. export default function () {
    9. return (
    10. <myContext.Provider value={100}>
    11. <div>hello worlddiv>
    12. <App />
    13. myContext.Provider>
    14. );
    15. }

     在 App 组件中我们获取到了父组件通过 context 传递来的 value 值。

  • 相关阅读:
    国际服务贸易重点整理
    电能质量监测装置及系统
    三个按键若何操作无线风力报警仪
    基于Windows编译someip
    月薪12K,蝶变向新勇往直前,我通过转行软件测试实现月薪翻倍...
    麒麟系统开发笔记(十二):在国产麒麟系统上编译GDAL库、搭建基础开发环境和基础Demo
    你的模型是最好的还是最幸运的?选择最佳模型时如何避免随机性
    python中的并发编程-进程、线程2
    python排序算法
    英语语法汇总(13.虚拟语气)
  • 原文地址:https://blog.csdn.net/qq_41482600/article/details/133981786