二、React进阶

一、代码分割

为什么要使用代码分割:在用webpack进行打包的时候,每个模块被导入合并在一个文件,这个文件叫做 bundle,这些 bundle 在一张页面上包括了整个APP。然而,当 APP 增长的时候,这些 bundle 尺寸开始变得越来越大,因此影响了页面加载时间

使用ant design pro的时候,打包出来的不是一个bundle文件,而是把每个模块打包成了一个async.js。如果用了代码拆分中的import(),就会把对应的js模块单独进行打包,打包出来的js模块名字实例如56.ffeb4024.async.js如果不用这个import(),是不会打包出这个模块的

代码拆分简单说就是动态加载组件

// 这里,OtherComponent 是不会请求直到MyComponent开始渲染。但是,因为我们静态导入了 OtherComponent,它会和 MyComponent 一起打包
import OtherComponent from './OtherComponent';
export defautl function MyComponent() {
  return (
    <div>
      <h1>My Component</h1>
      <OtherComponent />
    </div>
  )
}。

1、import()语法进行代码分割

  • 这个就是webpack的import()函数(还不是 JavaScript 语言标准的一部分,但是一个期望不久被接受的提案)。import()的语法十分简单。该函数只接受一个参数,就是引用包的地址,这个地址与es6的import以及CommonJS的require语法用到的地址完全一致。返回值为一个promise,在这个promise里面就可以访问对应的包了
  • 在代码中所有被import()的模块,都将打成一个单独的包,放在chunk存储的目录下。
  • 感觉这个只适用于加载的包只在一个地方使用的情况,因为只有在then的回调里面才可以访问对应的组件,所以不怎么实用。用这个import可以使浏览器运行到这一行代码的时候,才去加载对应的资源
  • 在antd design pro的.umi文件夹里面,就已经是用这个代码分割,来定义了路由的了,所以打包出来不是只有单个的chunk
// 下面import在打包的时候,会打包出来多个chunk
import(/* webpackChunkName: "moment" */ 'moment')
.then(({default: moment}) => {
  const tommorrow =moment().startOf('day').add(1, 'day');
  return tomorrow.format('LLL');
})
// 下面为umi中的使用。实现路由的懒加载
import { routerRedux, dynamic as _dvaDynamic } from 'dva';
const routes = [{
    path: '/user',
    component: __IS_BROWSER
      ? _dvaDynamic({
          component: () =>
            import(/* webpackChunkName: "layouts__UserLayout" */ '../../layouts/UserLayout'), // 这里就使用了import()语法
          LoadingComponent: require('F:/demo/ant design pro demo/src/components/PageLoading/index')
            .default,
        })
      : require('../../layouts/UserLayout').default,
}]

2、React.lazy()函数和React.Suspense 组件

这两个东西是配合使用的。

  • React.lazy() 函数接收一个函数,函数的返回值必须为一个代表react组件的promise。所以就刚好配合上面的import()一起用了,也可以说是对import()这种动态导入方式的优化
    • 被React.lazy()函数包裹的组件,在没有被使用的时候不会被打包进chunk,而是在被调用的时候才被打包
    • React.lazy()现在只支持默认导出
  • <React.Suspense>组件,这个组件里面包裹的是React.lazy()函数返回的promise组件。一个Suspense可以包裹很多的lazy组件
    • fallback属性:这个属性表示,当React.lazy()函数返回的promise组件的状态为pending的时候出现在页面上的占位符,当状态为resolve的时候,这个占位符会消失。
import React, { Suspense, lazy } from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}> // 当里面的组件的状态为pending的时候,页面显示<div>Loading...</div>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

二、Context

Context:Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。相当于提供了在所有子组件,或者孙子组件都可以获取的一个全局数据

缺点是,用了context之后,会使子组件的可复用性变差

下面说的都是在class组件中使用,如果想在函数组件中使用,则需要使用hook

1、React.createContext()函数

简单说这个函数就是用来创建context的。返回一个Context对象,这个对象里面包含Provider组件和一个Consumer组件。可以给 React.createContext() 传入参数,代表Provider组件的value的默认值,不过没什么必要

2、Provider组件

这个组件是用React.createContext()函数创建的context对象里面的。Provider组件用在父组件中,有一个value属性,代表需要在所有子组件间共享的值。用来包装子组件,被包裹的所有子组件,都可以通过Consumer组件或者contextType属性来获取到Provider组件上面的value属性值

3、Consumer组件

Consumer组件用在子组件中,里面的值为一个函数,函数的参数就是provider里面的value。但是,这个Consumer组件不是很好用,一般不用。

4、使用contextType属性替代Consumer组件

  • contextType属性: 这个是在子组件中使用,用来替代Consumer组件,即可以不用Consumer组件来获取Provider组件上面的value值。
    • 这个属性只能在class组件中使用,并且即使这个子组件有多个consumer ,contextType 也只能有一个。
    • 具体使用方法就是,在class组件中,static contextType = ThemeContext,后面的ThemeContext为包裹这个子组件的context对象。之后,在组件里面,就可以直接用this.context,来获取到provider里面的value值了
// context.js
import { createContext } from "react";
const MyContext = createContext(null);
const MyContext2 = createContext(null);
export {MyContext,MyContext2 };

// test.js
import React, {PureComponent, } from 'react';import { Button } from 'antd';import Test1 from './test1';
import {MyContext, MyContext2} from './context'; // 导入context
class Test extends PureComponent {
  state = {count: 1,go: 3333,};
  render () {return (<div>
          <Button onClick={()=>{this.setState({count: count + 1});}}>click me?????</Button>
            <MyContext.Provider value={this.state.count}> // 使用provider
              <MyContext2.Provider value={this.state.go}>
                <Test1 />
              </MyContext2.Provider>
            </MyContext.Provider>
        </div>);}
}

// test1.js
import React, {PureComponent,} from 'react';import Test2 from './test2';
class Test1 extends PureComponent {
  render () {return (<div><Test2></Test2></div>);}
}

// test2.js
import React, {PureComponent, useState, useEffect, useRef } from 'react';
import {MyContext, MyContext2} from './context'; // 依然要引入context
class Test2 extends PureComponent {
  static contextType = MyContext; // 使用contextType
  render () {
    console.log(this.context); // 这里就可以从this上面读取到对应的context了。 默认是1
    return (<div>
          <MyContext.Consumer> // 这里是在用consumer组件,不过一般是不用这个的
            {(value) =>(<div>res: {value}</div>)}
          </MyContext.Consumer>
        </div>);
  }
}

三、错误边界

部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界

如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息

但是由于错误边界只能用于class组件中,不能用于函数组件,所以还是不用这个

四、Fragments

React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。意思就是当一个react返回多个同级元素的时候,经常需要用一个父元素包着,这时候由于直接用div可能会改变结构,就直接用<React.Fragment>来包着

// 语法一
class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );}}

// 语法二,直接用一个类似于空标签的样式来写
class Columns extends React.Component {
  render() {
    return (
      <>
        <td>Hello</td>
        <td>World</td>
      </>
    );}}

五、高阶组件(HOC)

高阶组件缺陷,HOC需要在原组件上进行包裹或者嵌套,如果大量使用HOC,将会产生非常多的嵌套,简单说就是嵌套地狱,这让调试变得非常困难。所以可以把HOC和自定义hook配合使用,复用简单的逻辑就用自定义hook,而写插件,还是用hoc。

1、高阶函数

要谈高阶组件,首先就要谈高阶函数

高阶函数:高阶函数就是<1>接收一个或多个函数作为参数的函数。或者<2>返回值为一个函数的函数

  • 高阶函数实例:比如
    • <1>函数的回调。function funRequest(params){return Http.post(params.URL,params.data);}。返回的为一个函数
    • <2>函数柯里化。柯里化就是把,接受多个参数的函数,变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个也是返回一个函数
      • function add(a,b,c) {return (a + b + c);// 返回三个参数的和}。调用方式为add(1,2,3)
      • 柯里化之后为function addH(a) {return function (b) {return function (c) {return (a + b + c);}}}。调用方式为addH(1)(2)(3)
    • <3>函数节流去抖。这个就是接收一个函数,并且返回的也是一个函数

2、高阶组件

高阶组件:高阶组件和高阶函数是差不多的,高阶组件也是一个函数,只不过高阶组件的传入参数从函数变成了组件,而高阶组件的返回值也变成了一个组件

作用:高阶组件的作用简单说就是给传入的基础组件加上一些方法,或者改变传入的基础组件里面的一些方法。所以说高阶组件没有自定义hook好用呀,因为都是为了复用一些逻辑,自定义hook明显写法都要简单很多

比如dva里面的connect()的就是一个返回高阶组件的高阶函数,它先是接收一大堆的函数,然后返回一个高阶组件。这个高阶组件又接收一个组件,然后返回一个新组件,这个新组件的props被加上了一些属性。antd的Form.create()也是一个返回高阶组件的高阶函数。也可说高阶组件的使用范围还是挺广的

React 中的高阶组件主要有两种形式:属性代理反向继承

(1)属性代理
  • 属性代理简单说就是属性替代。高阶组件会修改基础组件的props,并且高阶组件还有自己还有state,用来强化基础组件。这种形式的高阶组件 返回的组件是继承至React.Component。这种方法的作用如下
      1. 操作 props
      1. 抽离 state
      1. 通过 ref 访问到组件实例
      1. 用其他元素包裹 传入的基础组件
// 下面就是属性代理形式的高阶组件
function AttributeAgentHigherOrderComponent2(BaseComponent) {
     return class extends React.Component{ // 这里是继承至React.Component
        constructor(props){
            super(props);
            this.state = {value:this.props.initValue || '',}
        }

        onValueChange = (event) => {
            let value = event.target.value.toString();
            // 这句最直观的体现什么是受控(要什么值显示什么值)
            value = `输入:${value === '输入' ? '' : value.replace('输入:','')}`;
            this.setState({value:value});
        }

        render(){
            const { value } = this.state;
            const newProps = { // 这里就是需要给基础组件加的新的props
              value: value,// input 的value属性
              eventOnChange:{
                  onChange: this.onValueChange,// input的onChange监听,方法在高阶组件内
              },
            }
            const props = Object.assign({},this.props,newProps);// 合成最新的props传给传入基础组件
            return (
                <div style={
   
   { backgroundColor: '#fafafa' }}> // 这里是给基础组件加上一个背景颜色,并且在基础组件前面加上一个字符串
                    啊啊啊啊啊啊啊啊:
                    <BaseComponent {...props}/>
                </div>
            )
        }
     }
export default AttributeAgentHigherOrderComponent2; // 导出高阶组件

// 下面为高阶组件的使用
import AttributeAgentHigherOrderComponent2 from 'AttributeAgentHigherOrderComponent2'
class PageContentWrapper extends PureComponent {
    render() {
        const { value, eventOnChange } = this.props; // 这里这个value和eventOnChange,就是经过高阶组件包裹之后,才在props上面出现的组件。就像props上面的dispatch属性,要经过connect包裹组件才会出现
    }
}
export default AttributeAgentHigherOrderComponent2(ControlInput); // 直接在导出的地方包着就好了
(2)反向继承
  • 反向继承这种形式的高阶组件 返回的组件是继承至传入的基础组件,所以根据继承的特性,继承可获取父类的所有静态资源,非私有属性和方法,且根据情况可对原方法进行重写。所以高阶组件可以操作基础组件的state并且可以进行渲染劫持。。这种方法的作用如下
      1. 操作 state。一般不用这个
      1. 渲染劫持(Render Highjacking) ,即高阶组件控制着 基础组件的渲染输出
      1. 重写基础组件中的方法
function ReverseInherit1(BaseComponent) {
    return class extends BaseComponent{ // 继承至传入的基础组件
         // 在这里重写基础组件中的提交函数
        toSubmit = () => {alert(`您要提交的值是:${this.state.value}`);}

        render(){
            const { value } = this.state;// 这里这个this.state就是基础组件中的state
            const superEle =  super.render();// 拿到父组件的要渲染的结构对象,做渲染劫持的关键。这里这个super就是class的语法,指的是父组件
            const newElement = React.cloneElement(superEle,this.props,superEle.props.children);
            if(value){// 这里就是渲染劫持,通过value值,来控制渲染的是什么
                return (
                    super.render()
                )
            }else{// value 有值则对原来的结构进行调整
                newElement.props.children.splice(1,1);
                return (newElement)
            }
        }
    }

六、深入 JSX

实际上,JSX 仅仅只是 React.createElement(component, props, …children) 函数的语法糖。

  • (1)由于 JSX 会编译为 React.createElement 调用形式,所以 React 库也必须包含在 JSX 代码作用域内。简单说就是,在使用JSX的文件里面,都需要引入React 库。自定义的组件的名字必须以大写字母开头;如果自定义的名字为小写开头,则会被看做一个HTML内置组件
import React from 'react'; // 只要使用了jsx,都需要引入这个库
<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>
// 上面代码会被编译为下面代码
React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)
  • (2)props.children: 这个是组件的props上面的一个属性。包含在开始和结束标签之间的 JSX 表达式内容将作为特定属性 props.children 传递给外层组件
    • JSX 会移除行内容中的首尾的空格以及空行。但是如果内容是false, null, undefined, true 等,它们并不会被渲染(如果实在是要传,那么需要把这东西构造成字符串)
// 父组件
import Child from 'Child';
function Parent(props) {
    return (
    <Child>dasdasdasdasda<Child>
    );
}
// 子组件
function Child(props) {
    const { children } = props; // 这里这个children就是代表上面的dasdasdasdasda
    return (
    <div>adsa</div>
    );
}
  • (3)React 组件也能够直接渲染出存储在数组中的第一层元素。所以才可以用map进行数组遍历,然后返回元素的
render() {
  // 不需要用额外的元素包裹列表元素!
  return [
    // 不要忘记设置 key :)
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

七、性能优化

React 内部使用几种巧妙的技术以便最小化 DOM 操作次数。对于大部分应用而言,使用 React 时无需专门优化就已拥有高性能的用户界面

  • 使用生产版本:开发版本的React 默认包含了许多有用的警告信息,这些警告信息在开发过程中非常有帮助但是这使得 React 变得更大且更慢;所以这时候就需要使用生产版本
    • 用Chrome插件React developer tools来判断处于哪个版本:如果处于 开发模式的网站,图标背景会变成红色;如果处于 生产版本的网站,图标背景会变成深色;

八、Portals

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。用处不大,不过antd的modal组件好像就有这个功能,可以选择挂载到父节点,或者挂载到body

ReactDOM.createPortal(child, container)。第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 需要挂载到的目标DOM 元素。

render() {
  // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
  // `domNode` 是一个可以在任何位置的有效 DOM 节点。
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

九、协调

本章讲的是diffing算法

算法设计动力:在某一时间节点调用 React 的 render() 方法,会创建一棵由 React 元素组成的树。在下一次 state 或 props或者context 更新时,相同的 render() 方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何有效率的更新 UI 以保证当前 UI 与最新的树保持同步

  • React 在以下两个假设的基础之上提出了一套 O(n) 的启发式算法
    • 两个不同类型的元素会产生出不同的树;
    • 开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定;

1、Diffing 算法

  • 当对比两颗树时,React 首先比较两棵树的根节点。不同类型的根节点元素会有不同的形态
    • (1)比对根节点为不同类型的html元素:React 会拆卸原有的树并且建立起新的树。拆卸一棵树时,对应的 DOM 节点和它们的状态都会被销毁
    • (2)比对根节点为同一类型的html元素:React 会保留 原有DOM 节点,仅比对及更新该DOM节点中有改变的属性。在处理完当前节点之后,React 继续对子节点进行递归
    • (3)比对同类型的组件元素: 当一个组件更新时,组件实例保持不变,这样 state就不需要重新渲染,只需要更新该组件实例的 props
  • 子节点进行递归与key: 在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;所以这种情况下,如果是从新列表中插入元素,但是后面的元素不变,则依然会修改后面所有的元素
    • Keys: 为了解决以上问题,React 支持 key 属性。当子元素拥有 key 时,React就可以判断哪些元素是以前就存在的仅仅需要移动一下,而哪些元素是新增的了。这个是性能优化的重要方法。而key值只需要在当前的列表中唯一就好了
  • 现在的diff算法,可以理解为一棵子树能在其兄弟之间移动,但不能移动到其他位置
  • 新旧两棵树做diff,就是现在这里的diff算法,时间复杂度O(n)。
    • 只比较同一层级,不跨级比较
    • tag 不同,则直接删掉重建,不再深度比较
    • tag 和 key都相同,被认为是相同节点,不再深度比较

十、Refs and the DOM

Refs 这个东西实际上就是,存储 当前元素DOM 节点的引用。简单说,就是给一个元素的ref属性绑定一个值,然后在其它地方访问这个值,就是访问到对应的元素的dom了

1、使用createRef()创建refs

  • 先使用 React.createRef() 创建一个refs;然后,把创建的refs值,加到标签的ref属性上面去
    • 访问 Refs: 当 ref 被传递给 render 中的元素时,访问该dom元素节点,可以通过ref 的 current 属性,如const node = this.myRef.current;
      • 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性
  • React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值
  • 当然使用hook也是可以创建ref的。useRef()就可以创建了
// 使用createRef()创建refs
  class Test extends React.Component {
    constructor(props) {
      super(props);
      this.myRef = React.createRef();
    }
    go() {
        this.myRef.current; // 使用下面的ref,这个即为绑定的那个dom
        console.log();
    }
    render() {
      return <div ref={this.myRef} onClick={()=>{this.go()}}>11111111111111111111111</div>; // ref属性加到这里来了
    }
  }

2、使用回调 Refs方式创建refs

不同于createRef() 创建的 ref ,回调refs方式是给元素对应的ref属性传递一个函数,而这个函数接收的参数,就是当前元素的dom节点

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.setTextInputRef = element => { // 这里就是用回调方式创建的refs,接收的element就是当前挂载的元素的dom节点
      this.textInput = element;
    };
  }

  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input type="text" ref={this.setTextInputRef} />
      </div>
    );
  }
}

3、React.forwardRef(Component)

这东西接收一个 Component,返回要给Component。作用为:让ref可以直接放到函数组件上面,并且获取到这个函数组件; 即让ref可以作为参数 传到函数组件里面去,然后来绑定到对应的dom

因为,如果在一个class组件中使用一个函数组件,在函数组件中使用ref,是获取不到这个函数组件的; 所以就需要使用这个 React.forwardRef 来包裹对应的函数组件,并且把 ref作为函数组件的参数 传入进去; 就可以在使用函数组件的时候,用ref获取到这个函数组件了

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
class a extends PureComponent {
    constructor() {
        const ref = React.createRef();
    }
    render() {
     // 在这里使用 ref 来获取函数组件,普通情况下是获取不成功的; 但是这里用了React.forwardRef,就可以获取成功了
        return <FancyButton ref={ref}>Click me!</FancyButton>;
    }
}


十一、Render Props

“render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。简单说就是在父组件上面,接收一个属性为一个函数,这个函数返回一个 React 元素。然后再在子组件上面调用这个属性,就可以渲染出对应的react元素了

简单说这个Render Props也是为了复用代码,不过好像没有高阶组件好用。这个主要是在子组件中复用一个公共组件的,用处好像不大

render prop 会抵消使用 React.PureComponent 带来的优势。因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值

class Cat extends React.Component {
  render() {const mouse = this.props.mouse;return (<img src="/cat.jpg" style={
   
   { position: 'absolute', left: mouse.x, top: mouse.y }} />);}
}

class Mouse extends React.Component {
  constructor(props) {super(props);this.handleMouseMove = this.handleMouseMove.bind(this);this.state = { x: 0, y: 0 };}
  handleMouseMove(event) {this.setState({x: event.clientX,y: event.clientY});}
  render() {return (<div style={
   
   { height: '100vh' }} onMouseMove={this.handleMouseMove}>
  {this.props.render(this.state)} // 这里就是在子组件中使用render props,直接调用,然后就渲染出来了一个react元素
  </div>);}
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div><h1>移动鼠标!</h1>
        <Mouse render={mouse => ( // 这里就是用了render Props。是在组件上面写了一个属性,这个属性返回了一个react元素
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

十二、严格模式(StrictMode)

严格模式(StrictMode)是用来检查代码里面的问题的。被<React.StrictMode>包裹的所有元素和它们的后代元素,都会被进行严格模式检查。检查出来的错误,依然是会输出在控制台上面的。感觉还是很好用的,不过有了eslint来检查语法问题,是不是就不需要了???

严格模式检查仅在开发模式下运行;它们不会影响生产构建。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI

  • 严格模式检查的内容:
    • (1)识别不安全的生命周期: 主要是指识别过期的API。React 会列出使用了不安全生命周期方法的所有 class 组件,并打印一条包含这些组件信息的警告消息
    • (2)关于使用过时字符串 ref API 的警告
    • (3)关于使用废弃的 findDOMNode 方法的警告
    • (4)检测意外的副作用:这个即检查是否在更新的生命周期函数中,进行了副作用操作(这样可能会带来性能问题)
      • 函数副作用:指的是当调用函数时,除了返回函数值之外,还做了和本身运算返回值无关的事。即console.log()、操作 DOM、http 请求、修改全局变量等,都属于副作用
    • (5)检测过时的 context API
function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode> // 这里面包裹的所有元素和它们的子元素都会被进行严格检查没有被React.StrictMode包裹的组件就不会被进行严格检查
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

八、非受控组件

受控组件与非受控组件都是相对于form元素来说的,受控元素就是组件上有一个value值(这个值存在state里面),非受控组件就是组件上没有value属性(这个值存在对应的真实dom上面)。具体请看基础部分

一般情况下使用form元素都是使用受控组件的,但是下面来介绍几种非受控组件的使用方法。使用非受控组件的优点就是写着快,可以减少代码量

  • 使用方法:非受控组件的使用方法就是用ref来绑定这个组件,然后获取这个ref,就可以获取到非受控组件上面的值了
    • 如果想要给这个非受控组件设置默认值,就直接用defaultValue就好了。defaultValue的值是会被后续输入的值和value值覆盖的,所以只能用作默认值
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.inputValue = React.createRef();
  }
  handleSubmit(event) {
    alert('A name was submitted: ' + this.inputValue.current.value); // 这里直接获取ref,就可以获取到对应非受控组件的值了
  }
  render() {
    return (
      <form onSubmit={()=>{this.handleSubmit()}}>
          <input type="text" ref={this.inputValue} /> // 这里就是非受控组件的使用方法,绑定ref
      </form>
    );
  }
}

猜你喜欢

转载自blog.csdn.net/rocktanga/article/details/121348177