一、代码分割
为什么要使用代码分割:在用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。这种方法的作用如下
- 操作 props
- 抽离 state
- 通过 ref 访问到组件实例
- 用其他元素包裹 传入的基础组件
// 下面就是属性代理形式的高阶组件
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并且可以进行渲染劫持。。这种方法的作用如下
- 操作 state。一般不用这个
- 渲染劫持(Render Highjacking) ,即高阶组件控制着 基础组件的渲染输出
- 重写基础组件中的方法
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>
);
}
}