React基础知识一

写的东西太多了,照成csdn文档编辑器都开始卡顿了,所以分篇写。

1.安装React

需要安装下面三个包。

react:react核心包
react-dom:渲染需要用到的核心包
babel:将jsx语法转换成React代码的工具。(没使用jsx可以不装)

1.1 在html中引入CND

在html中使用react是一种简便的方式。非常有利于初学者学习。
地址为:https://zh-hans.react.dev/learn/installation
需要引入下面这三个CDN
注意:这三个顺序是不能变的

    <script src="https://unpkg.com/react@18/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script><!-- Don't use this in production: --><script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

1.2 React初体验

1.2.1 JSX写法(掌握)

在html里面添加下面的代码。注意一定要添加type="text/babel"不然是会不识别语法的。

<body><div id="root"></div><script type="text/babel">//1.创建虚拟DOMconst VDOM=<h1>Hello React</h1>//或const VDOM = (//不加小括号换行也是不会报错的,仅仅是为了结构好管理<h1><span>Hello React</span></h1>)//2.渲染虚拟DOM到页面ReactDOM.render(VDOM,document.getElementById('root'));</script>
</body>
1.2.2 React原始写法(了解)

下面这种写法可以不使用babel,也就不用引用babel包,所以类型也可以写成type=“text/javascript”。
这是react提供的语法糖,react自己的原始代码都是这种形式写的。对于开发者来说,这种语法是非常难用的。JSX就是react为我们提供的方便使用的语法。
下面的写法不用手动写,开发就用JSX,但是React会把JSX转化成下面的写法,了解一下还是很有必要的。

<body><div id="root"></div><div id="root2"></div><script type="text/javascript">//1.创建虚拟DOMconst VDOM = React.createElement('h1',{id:"title"},"Hello React")//嵌套多层的时候非常的麻烦const VDOM2 =  React.createElement('h1',{id:"title"},React.createElement('span',{},"Hello React"))//2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById('root'));ReactDOM.render(VDOM2, document.getElementById('root2'));</script>
</body>
1.2.3组件写法(后面掌握)

这种写法可以先跳过,后面学组件的时候再说。

    <div id="root"></div><script type="text/babel">function MyApp() {//这可以理解为一个组件return <h1>Hello, world!</h1>;//不要奇怪,这是jsx语法,就是支持这样写的}const container = document.getElementById('root');const root = ReactDOM.createRoot(container);//18以前这个函数名字叫renderroot.render(<MyApp />);</script>

运行一下,就有hello world了。
在这里插入图片描述
上面的代码可以这样写,实现一样的效果。

      const container = document.getElementById('root');const root = ReactDOM.createRoot(container);//18以前这个函数名字叫renderroot.render(<h1>Hello, world!</h1>);

1.3 真实DOM和虚拟DOM的区别

我们把虚拟DOM直接输出,可以看到虚拟DOM就是一个普通的js对象。

        const VDOM = React.createElement('h1', { id: "title" }, "Hello React")console.log("虚拟DOM:",VDOM);

在这里插入图片描述
真实DOM可以通过下面的方式输出。

        const realDOM=document.getElementById("root")console.log("真实DOM:",realDOM);

可以看到在浏览器直接把内容给输出了。
在这里插入图片描述
这时候我们需要debugger断点来看看他的真实内容是怎么样的。
可以看到,真实DOM是有非常多的属性的,而虚拟DOM的属性实际上非常的少。
在这里插入图片描述
或者使用console.dir也是可以直接输出的。g

console.dir(realDOM);

小结:
1.虚拟DOM本质上是一个普通对象。
2.虚拟DOM比较轻,真实DOM比较重,因为react不需要那么多属性。
3.虚拟DOM最终被react转化为真实DOM,呈现在页面上。

2.JSX语法规则

1.定义虚拟DOM时,不要写引号

const VDOM=<h1>Hello React</h1>

2.标签中引入JS表达式需要用{}
不需要加引号,直接跟{}

    const myId="title"const myData="Hello World"const VDOM = (<h1 id={myId}><span>{myData}</span></h1>)

3.不允许写class这个属性,而要写className
这是因为class是一个ES6类的关键词,React为了避开这个,自己定义了className。

.title{background:orange
}
//直接跟类名
<h1 className="title">Hello React</h1>

4.style不能写成字符串的形式,而是要写成对象的形式
需要大括号包裹。注意下面的{{}}并不是类似vue的mustache语法,而是和变量外面要使用{}是一样的道理,里面嵌套的{}表示的是这是一个对象。

<h1 style={{color:'lightblue',fontSize='29px'}}>Hello React</h1>

5.虚拟DOM只能有一个根元素
6.标签必须闭合

<input type="text" />
或者
<input type="text"><input/>

7.标签首字母
(1).若小写字母开头,则将该标签转为html同名元素,若html中无该标签对应的同名元素,则报错
(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
注意: 你写的这些div标签都是JSX标签,不是html标签,或者说JSX给你提供了类似html的语法,最终他会转变成html标签在浏览器展示。

3.循环列表

注意,循环只能使用map这种表达式,不可以用for循环,因为for循环是语句。
{}里面是只能写js表达式,不能写js语句,所以for和if判断都是不可以写的。

这里必须需要理解清楚表达式语句的区别
表达式: 表达式会产生一个值,这个值可以放在任意一个需要值的地方。

a
a+b
foo(1)
arr.map()
console.log("name")
function test(){}//方法也是表达式,也是有返回值的

语句(代码):

if(){}
for(){}
switch(){}
<body><div id="root"></div><script type="text/babel">const data = ['Angular', 'React', 'Vue']const VDOM = (<div><h1>遍历列表</h1><ul>{data.map((item,index)=>{//这种方式实现key是不太合适的,后面再说return <li key={index}>{item}</li>})}</ul></div>)//2.渲染虚拟DOM到页面ReactDOM.render(VDOM, document.getElementById('root'));</script>
</body>

4.组件编程

4.1 react浏览器插件安装

推荐使用edge浏览器。下载方便,注意需要把下面两个勾打上,不然本地路径图标是不会亮的。
在这里插入图片描述
f12刷新一下开发者工具可以看到Components和Profiler这两个功能模块。
在这里插入图片描述

4.2 函数式组件

需要注意的是函数名字需要大写,不然报错。render函数的第一个参数需要写成标签的形式,不然报错。

        <div id="root"></div><script type="text/babel">function MyComponent(){return <div>我是一个函数式组件(适用于简单组件的定义)</div>}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

在这里插入图片描述

4.2.1 函数式组件this的指向

this指向是undefined,因为react在被babel翻译的时候开启了严格模式。

4.3 类式组件

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{render(){return <div>这是一个类式组件(用于复杂的场景)</div>}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

1.react解析MyComponent标签,找到MyComponent组件。
2.发现组件是类定义的,new出该类的实例,通过实例调用render方法。
3.将render方法返回的虚拟DOM转化为真实DOM,在浏览器上呈现。

render里面的this是组件实例对象。

5.组件实例三大属性之state

简单使用

props必写,不写报错。继承必须调用构造器,这是class规定的。直接通过this.state赋值就可以了。

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:false}}render(){return <div>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

回调事件

通过onClick指定点击函数。

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:false}}render(){return <div onClick={changeWeather}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}}function changeWeather(){console.log("被点击了");}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

修改state的值

点击事件虽然有了,但是如果我们想要通过下面的代码来修改state的值的话是会报错的。因为this是undefined。

             function changeWeather(){console.log("被点击了");this.state.isHot=false}

我们可以定义一个that变量,在构造器里面把this赋值给that,这样就可以在外面获取到组件实例对象了。

        <div id="root"></div><script type="text/babel">let thatclass MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:true}that=this}render(){return <div onClick={changeWeather}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}}function changeWeather(){console.log("被点击了",that);// that.state.isHot=false}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

这种方式虽然可行,但是非常的不合理。因为写的代码都是零散的,没有组件的封装性。
试着把代码改成下面这个样子。这里存在非常多的问题。
1.onClick={this.changeWeather},需要写this.changeWeather而不是直接写changeWeather。这是因为现在changeWeather是写在类里面,而写类外面是直接调用。
2.render和constructor的this是组件的实例对象,而changeWeather的this却是undefined。

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:true}}render(){return <div onClick={this.changeWeather}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}changeWeather(){console.log("被点击了",this);// that.state.isHot=false}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

正确的做法: (没搞懂)
添加this.changeWeather=this.changeWeather.bind(this)这一行就可以了。

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:true}this.changeWeather=this.changeWeather.bind(this)}render(){return <div onClick={this.changeWeather}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}changeWeather(){console.log("被点击了",this);// that.state.isHot=false}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

也可以写成下面这个样子:(没搞懂)

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:true}this.demo=this.changeWeather.bind(this)}render(){return <div onClick={this.demo}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}changeWeather(){console.log("被点击了",this);// that.state.isHot=false}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

通过setState修改值

直接修改值是没有响应式的,值虽然修改成功了,但没有响应式。

        <div id="root"></div><script type="text/babel">class MyComponent extends React.Component{constructor(props){//props必写,不写报错super(props)//继承必须调用构造器,这是class规定的this.state={isHot:true}this.demo=this.changeWeather.bind(this)console.log(this);}render(){return <div onClick={this.demo}>今天天气很{this.state.isHot?"炎热":"凉爽"}</div>}changeWeather(){console.log("被点击了",this);// this.state.isHot=!this.state.isHotthis.setState({isHot:!this.state.isHot})}}//2.渲染虚拟DOM到页面ReactDOM.render(<MyComponent/>, document.getElementById('root'));</script>

setState这个方法是来自React.Component这个类的,可以通过原型链查看到。
在这里插入图片描述
setState会合并对象而不是替换对象。也就是可以指定属性进行修改。

state的精简写法(最正确的写法 )

1.首先,利用class的特性,写在类里面的属性会直接挂载到类实例对象上。构造器可以不用写,直接定义一个state属性就可以。
2.每定义一个点击回调函数都要在构造器里面写类似this.changeWeather=this.changeWeather.bind(this)这样的代码,非常的麻烦。可以利用箭头函数,会自动向上查找this的特性完美的处理this指向问题。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return <div onClick={this.changeWeather}>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</div>}changeWeather = () => {this.setState({ isHot: !this.state.isHot })}}ReactDOM.render(<MyComponent />, document.getElementById('root'));</script>

小结

state是三大属性中最难最重要的。掌握了state,最难的骨头已经啃下来了。

6.组件实例三大属性之props

基本使用

非常简单,没什么问题。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><ul><li>姓名:{this.props.name}</li><li>年龄:{this.props.age}</li></ul></div>)}changeWeather = () => {this.setState({ isHot: !this.state.isHot })}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

批量传递props

写成下面这样就可以用对象来批量传递属性了。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><ul><li>姓名:{this.props.name}</li><li>年龄:{this.props.age}</li></ul></div>)}changeWeather = () => {this.setState({ isHot: !this.state.isHot })}}const p={name:"Tom",age:18}ReactDOM.render(<MyComponent {...p} />, document.getElementById('root'));</script>

细节:
这里有个细节需要讲一下。{…p}并不是展开对象。
在纯js里面,下面这样的写法是错误的,对象是不能通过…展开的,数组可以,需要是可迭代对象。

        const p={name:"Tom",age:18}console.log(...p);

在纯js里面,下面的写法是正确的,这是es6创建对象的一种方式。相当于复制对象。

        const p={name:"Tom",age:18}const p2={...p}console.log(p2);

但如果是在react标签里面,{}是react的表达式符号,不是对象,…p是不能展开对象的,按照js的语法的话,但在react里面可以,这是babel和react帮我们实现的。

<MyComponent {...p} />

在react里面写下面的代码,不会报错,但也没内容,这是react做了处理的结果。

        const p={name:"Tom",age:18}console.log(...p);

props类型限制

在react15之前是不需要额外引用包的。
是直接写在react包里面的,后面抽离了出去。基本没人用React15了。也不会有这种写法了。

MyComponent.propTypes = {name: React.PropTypes.string}

新写法:react16以后
需要引入这个包。

<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script>

类型定义和默认值,没什么好说的。

        MyComponent.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,speak: PropTypes.func}MyComponent.defaultProps = {name: "Tom",age: 19}

props的精简写法

把原本写在外面的写到类里面,并加上static就行了。
加static相当于直接挂载到类本身上,而不是实例对象上。

        class MyComponent extends React.Component {state = { isHot: true }static propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,speak: PropTypes.func}static defaultProps = {name: "Tom",age: 19}render() {return (<div><ul><li>姓名:{this.props.name}</li><li>年龄:{this.props.age}</li></ul></div>)}changeWeather = () => {this.setState({ isHot: !this.state.isHot })}}

构造器和props

构造器可以不写,但是写了必须写props和super,不然会出现在构造器里面调用this.props出现undefined的bug。这个官方文档里面明确写的。

            constructor(props){super(props)console.log(this.props);//不写和不传super,this.props是undefiend}

这种情况是非常少见的,几乎不会出现。一是你不用写构造器也可以在别的地方使用this.props,二是如果你写了构造器,那么参数的props是可以直接使用的,不需要使用this.props。

函数式组件中使用props

在函数式组件里面,我们只能玩玩props,state和refs是玩不了的,因为没有this。而且props之所以能玩也是因为函数能传参数这个特性。类型限制只能写在外面。

    <div id="root"></div><script type="text/babel">function Person(props) {return (<div><ul><li>姓名:{props.name}</li><li>年龄:{props.age}</li></ul></div>)}//类型限制只能写在外面Person.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,speak: PropTypes.func}Person.defaultProps = {name: "Tom",age: 19}const p = { name: "Tom", age: 18 }ReactDOM.render(<Person name="Jack" age="20" />, document.getElementById('root'));</script>

7.组件实例三大属性之refs

7.1 基本使用(字符串类型的ref)

通过下面的代码使用基本功能:
1.点击按钮显示输入框内容。
2.输入框焦点离开,显示输入框内容。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><input id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;<input id="input2" placeholder="" onBlur={this.showData2} />&nbsp;</div>)}showData = () => {const input=document.getElementById("input1")alert(input.value)}showData2 = () => {const input=document.getElementById("input2")alert(input.value)}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

上面代码是通过原生getElementById实现的,不太合适。我们希望通过react实现。react的refs就可以实现类似的功能。

react的refs是用来给元素打标识用的。

修改后的代码如下,非常的简单。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><input ref='input1' id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;<input ref='input2' id="input2" placeholder="" onBlur={this.showData2} />&nbsp;</div>)}showData = () => {// const input=document.getElementById("input1")const {input1}=this.refsalert(input1.value)}showData2 = () => {// const input=document.getElementById("input2")const {input2}=this.refsalert(input2.value)}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

在这里插入图片描述
注意:这里获取到的refs的内容是真实DOM节点的内容。
我们输出this看下refs长什么样子。是一个键值对,key是ref的名字,value是元素的类型加元素的id。
没有写id的话,我都不知道后面会跟id。
在这里插入图片描述

7.2 回调形式的ref

字符串形式的ref是最简单的一种ref。但是官方不推荐使用了,因为存在效率问题。不过如果你用的是16.8版本的话,继续用也没什么大问题,毕竟用起来简单。

下面是回调函数的用法,个人觉得不好用,写起来冗余和麻烦。

    <script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {console.log(this);return (<div><input ref={e=> this.input1 = e} id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;<input ref={e=> this.input2 = e} id="input2" placeholder="" onBlur={this.showData2} />&nbsp;</div>)}showData = () => {const { input1 } = thisalert(input1.value)}showData2 = () => {const { input2 } = thisalert(input2.value)}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

7.3 回调形式ref的调用次数问题

这个在官方文档里面有提到,在写内联函数的情况下,第一次渲染的时候会调用一次,当页面刷新的时候,会调用两次,第一次内联函数的参数值是null(被重置了),第二次是正常值。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><span>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</span><input ref={e => { this.input1 = e; console.log("@:", e); }} id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;</div>)}showData = () => {this.setState({isHot: !this.state.isHot})}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

在这里插入图片描述
写成绑定函数的形式就不会出现这种问题了,但这个问题是无关紧要的。下面的这种写法非常的麻烦,完全没有必要。 用内联回调函数的形式是完全没有问题的。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }render() {return (<div><span>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</span><input ref={this.saveInput} id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;</div>)}showData = () => {this.setState({isHot: !this.state.isHot})}saveInput = (e)=>{this.input1=econsole.log("@:",e);}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

7.4 通过createRef创建ref (推荐)

这种方式是官方推荐的,用起来不是很麻烦,也解决了性能问题。不过最简单还是字符串的形式。不过发生性能问题就不好说了。字符串形式还是不要使用比较好。

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = { isHot: true }myRef1=React.createRef()myRef2=React.createRef()render() {return (<div><span>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</span><input ref={this.myRef1} id="input1" placeholder="请输入内容" />&nbsp;<button onClick={this.showData}>点击显示内容</button>&nbsp;<input ref={this.myRef2} onBlur={this.showData2} id="input1" placeholder="请输入内容" />&nbsp;</div>)}showData = () => {alert(this.myRef1.current.value)}showData2 = () => {alert(this.myRef2.current.value)}}ReactDOM.render(<MyComponent name="Tom" age="18" />, document.getElementById('root'));</script>

react脚手架使用和搭建

后面的有些内容不用脚手架建立工程项目的话使用起来不是很方便。或者不知道怎么使用,也不想花费大量的时间研究可能用不上的使用方式。所以用工程项目还是最有性价比的选择。

手动搭建react项目

通过下面的命令创建一个脚手架项目:

npm install -g create-react-app
create-react-app react-demos

默认目录结构如下:
在这里插入图片描述
这些内容可以作为参考,但是现在我们把src下的内容全部删掉,因为我们想要重头搭建我们的项目。
在这里插入图片描述
这时候我们再运行会告诉我们没有index.js。这是因为webpack需要index.js作为程序的入口。
在这里插入图片描述
非常神奇的是,你只要添加一个空白index.js文件,项目就可以运行的,只不过页面是空白的。
我们往index.js添加下面的内容,其中#root是index.html中的根元素,webpack自己会取找。

import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(<h1>Hello React</h1>);

跑起来我们就可以看到下面的内容。
在这里插入图片描述
我们可以定义一个App类组件来封装我们的内容。

import ReactDOM from "react-dom/client";
import React from "react";
class App extends React.Component {render() {return <h1>Hello React</h1>;}
}
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(<App />);

也可以把App组件单独写到外面。

import React from "react";
class App extends React.Component {render() {return <h1>Hello React</h1>;}
}
export default App;
import ReactDOM from "react-dom/client";
import React from "react";
import App from "./App";
const root = ReactDOM.createRoot(document.querySelector("#root"));
root.render(<App />);

我们可以定义自己的组件,然后在App.jsx里面引入

import React from "react";
class HelloReact extends React.Component {render() {return <h1>Hello React</h1>;}
}
export default HelloReact;

这样,我们的工程就搭建完成了。

import React from "react";
import HelloReact from "./HelloReact";
class App extends React.Component {render() {return (<div><HelloReact /></div>);}
}
export default App;

通过eject命令查看webpack配置

我们可以看到react脚手架创建的项目实际上是通过react-scripts这个工具来运行的。而webpack的配置就在这个包里面的。

  "scripts": {"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test","eject": "react-scripts eject"},

可以看到webpack的配置在这个包下面。
在这里插入图片描述
react-scripts这个工具提供了eject命令来暴露webpack的配置,但是一般不推荐这么做。一是因为webpack的配置非常复杂且难懂,二是一般我们专注开发,不要花太多精力在这些东西上。

一定要查看和修改也是有办法的,执行下面的命令:

npm run eject

会弹出警告,问你要不要弹出配置文件,这个操作是不可逆的。
在这里插入图片描述
执行完后,会多出config和scripts两个文件夹。
在这里插入图片描述
scripts节点的内容也变成下面的内容。

  "scripts": {"start": "node scripts/start.js","build": "node scripts/build.js","test": "node scripts/test.js"},

整个package.json都变了。依赖什么都一股脑的写道里面,非常的乱。所以还是不要弹出配置比较好。

使用craco来管理配置

这是一个第三方工具。主流的工具,大家都在用。

组件的生命周期

生命周期是非常重要的内容,我们需要在正确的生命周期做正确的事情。
这么这个网站是react官方声明周期图的地址,16.8后,react在官方文档里面已经移除了到这个图的导航。
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

这个图有两个版本,一个简单版,一个显示不常用生命周期版本。如果只看简单版的话,react的生命周期还是比较简单的。
在这里插入图片描述
不常用生命周期:
在这里插入图片描述

组件挂载和componentDidMount方法

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {constructor(props) {super(props);console.log("constructor");}render() {console.log("render");}componentDidMount() {console.log("compoentDidMount");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

组件先执行constructor,再执行render,再执行componentDidMount。
在这里插入图片描述

父子组件的componentDidMount方法

如果是父子组件的话,情况就复杂一点。

      class MyComponent extends React.Component {constructor(props) {super(props);console.log("constructor");}render() {console.log("render");return <SubComponent />;}componentDidMount() {console.log("compoentDidMount");}}class SubComponent extends React.Component {constructor(props) {super(props);console.log("sub constructor");}render() {console.log("sub render");}componentDidMount() {console.log("sub compoentDidMount");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));

父组件的componentDidMount是最后执行的。但父组件是先被挂载的。之所以会出现父组件的componentDidMount后执行是因为使用的是深度优先遍历算法。
在这里插入图片描述

更新数据时的生命周期和componentDidUpdate方法

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {message: "this is message",};constructor(props) {super(props);console.log("constructor");}changeMessage = () => {this.setState({message: "message changed",});};render() {console.log("render");return (<div><h1>{this.state.message}</h1><button onClick={this.changeMessage}>changeMessage</button></div>);}componentDidMount() {console.log("compoentDidMount");}componentDidUpdate() {console.log("compoentDidUpdate");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

更新数据的时候,render函数会被多次调用。并且再render执行完成后,都会调用componentDidUpdate方法。
在这里插入图片描述

更新数据时的生命周期和componentDidUpdate方法——子组件更新

    <div id="root"></div><script type="text/babel">class MyComponent extends React.Component {constructor(props) {super(props);console.log("constructor");}render() {console.log("render");return (<div><SubComponent /></div>);}componentDidMount() {console.log("compoentDidMount");}componentDidUpdate() {console.log("compoentDidUpdate");}}class SubComponent extends React.Component {state = {message: "this is message",};constructor(props) {super(props);console.log("sub constructor");}changeMessage = () => {this.setState({message: "message changed",});};render() {console.log("sub render");return (<div><h1>{this.state.message}</h1><button onClick={this.changeMessage}>changeMessage</button></div>);}componentDidMount() {console.log("sub compoentDidMount");}componentDidUpdate() {console.log("sub compoentDidUpdate");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

可以看到。当子组件内容被修改的时候,只会调用,子组件的更新方法,父组件是不会调用更新方法的,也不会调用render。

在这里插入图片描述

更新数据时的生命周期和componentDidUpdate方法——父组件更新

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {message: "this is message",};constructor(props) {super(props);console.log("constructor");}changeMessage = () => {this.setState({message: "message changed",});};render() {console.log("render");return (<div><h1>{this.state.message}</h1><button onClick={this.changeMessage}>changeMessage</button><SubComponent /></div>);}componentDidMount() {console.log("compoentDidMount");}componentDidUpdate() {console.log("compoentDidUpdate");}}class SubComponent extends React.Component {constructor(props) {super(props);console.log("sub constructor");}render() {console.log("sub render");}componentDidMount() {console.log("sub compoentDidMount");}componentDidUpdate() {console.log("sub compoentDidUpdate");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

父组件内容修改的时候,子组件的render和componentDidUpdate也是会一起调用的,也就是子组件会重新渲染,因为子组件可能依赖了父组件的数据。react选择直接重新渲染子组件。
在这里插入图片描述

组件卸载和componentWillUnmount方法

在组件被销毁的时候,componentWillUnmount会被调用。我们需要在componentWillUnmount做一些回收资源的事情。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {show: true,};constructor(props) {super(props);// console.log("constructor");}changeShow = () => {this.setState({show: false,});};render() {// console.log("render");const { show } = this.state;return (<div>{show && <SubComponent />}<button onClick={this.changeShow}>changeShow</button></div>);}}class SubComponent extends React.Component {state = {show: true,};constructor(props) {super(props);console.log("sub constructor");}changeShow = () => {this.setState({show: false,});};render() {console.log("sub render");return <div>我是子组件</div>;}componentDidMount() {console.log("sub compoentDidMount");}componentDidUpdate() {console.log("sub compoentDidUpdate");}componentWillUnmount() {console.log("sub componentWillUnmount");}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

不常用的生命周期

不常用也是官方文档说的。
在这里插入图片描述

shouldComponentUpdate方法

如果shouldComponentUpdate返回false,那么组件的render和componentDidUpdate将不会被调用。这个可以用于一些性能优化的地方。禁止一些组件的重新渲染。
从图中也可以很明显看到,render和componentDidUpdate这两个函数是在shouldComponentUpdate之后执行的,如果shouldComponentUpdate返回false,那么后面的生命周期将不再执行。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {message: "this is message",};constructor(props) {super(props);console.log("constructor");}changeMessage = () => {this.setState({message: "message changed",});};render() {console.log("render");return (<div><h1>{this.state.message}</h1><button onClick={this.changeMessage}>changeMessage</button></div>);}componentDidMount() {console.log("compoentDidMount");}componentDidUpdate() {console.log("compoentDidUpdate");}shouldComponentUpdate() {return true;}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>
getsnapshotbeforeupdate方法

官方文档给了下面的例子。
https://zh-hans.legacy.reactjs.org/docs/react-component.html#getsnapshotbeforeupdate

处理列表滚动的时候可以用到,是伪代码。

  <body><div id="root"></div><script type="text/babel">class ScrollingList extends React.Component {constructor(props) {super(props);this.listRef = React.createRef();}getSnapshotBeforeUpdate(prevProps, prevState) {// 我们是否在 list 中添加新的 items ?// 捕获滚动​​位置以便我们稍后调整滚动位置。if (prevProps.list.length < this.props.list.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {// 如果我们 snapshot 有值,说明我们刚刚添加了新的 items,// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}render() {return <div ref={this.listRef}>{/* ...contents... */}</div>;}}ReactDOM.render(<ScrollingList />, document.getElementById("root"));</script></body>

组件通信

组件通信父传子props

这个其实没什么好讲的,直接使用props就可以了

组件通信子传父

在vue或者uniapp里面,子传父有类似$emit,$on这样的发送接收方法。但在react里面,这些都是没有的。react实现的方式就是纯js的方式,是利用回调函数机制来实现的。
我们需要在子组件上加一个回调方法。

<AddCounter onAdd={(n) => this.handleAdd(n)} />

完整逻辑如下。这里需要注意的地方就是函数的命名规范。例如add方法,你可以在父子组件里面都全部取名叫add,如果你自己能够分得清的话。
命名规范建议是,点击事件用clickXXX,组件回调函数用onXXX,而父组件处理函数用handleXXX。

    <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {counter: 0,};constructor(props) {super(props);console.log("constructor");}handleAdd = (num) => {this.setState({counter: this.state.counter + num,});};render() {const { counter } = this.state;return (<div><div>计数结果是:{counter}</div><AddCounter onAdd={(n) => this.handleAdd(n)} /></div>);}}class AddCounter extends React.Component {constructor(props) {super(props);console.log("constructor");}clickAdd = (n) => {this.props.onAdd(n);};render() {return (<div>{/*要写箭头函数,不能直接执行 直接执行会在在渲染的时候就调用了 */}<button onClick={() => this.clickAdd(1)}>+1</button><button onClick={() => this.clickAdd(5)}>+5</button></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

tabs切换的组件通信案例(简单实现版)

写在一个组件里面,实现基本功能。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="https://unpkg.com/react@18/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script><!-- Don't use this in production: --><script src="https://unpkg.com/@babel/standalone/babel.min.js"></script><style>.tabs {display: inline-block;padding: 10px;}.active {color: pink;border-bottom: 2px solid pink;}</style></head><body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {tabs: [{name: "电脑",},{name: "手机",},{name: "电视",},],curIndex: 0,};clickTab = (index) => {this.setState({curIndex: index,});};constructor(props) {super(props);console.log("constructor");}render() {const { tabs, curIndex } = this.state;return (<div>{tabs.map((item, index) => {return (<spanclassName={`tabs ${index === curIndex ? "active" : ""}`}key={index}onClick={(e) => this.clickTab(index)}>{item.name}</span>);})}<div>{tabs[curIndex].name}</div></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>
</html>

在这里插入图片描述

tabs切换的组件通信案例(组件封装版)

PureComponent类和memo高阶函数

这个类是React提供的封装好的优化类。只有组件的props或者state发生改变的时候,才会重新渲染组件。源码实现是通过浅层比较实现,也就是只比较对象的第一层。

对于函数式组件来说,是没有生命周期的,也就不能实现类似PureComponent这样的封装效果,但是React提供了memo函数来实现这个功能。

开发的时候,你是一定要用到这两个东西的,不知道的话你一定是一个新手。官方文档性能优化是有提到这两个东西的。

hooks

什么是hooks? (coderwhy)

hooks是react 16.8(2019年)出的新特性。
react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候,函数式组件是非常鸡肋的,几乎没什么用。因为函数式组件不能保存数据状态,所以只能用于一些简单的展示的场景,传什么数据就展示什么数据(因为只有props是可以用的)。并且函数组件是没有生命周期的。

但因为函数式编程的思想在前端是普遍流行和推崇的。我猜想react团队在设计函数式组件的时候肯定已经想到了这个问题。但可能当时没有想到合适方式实现函数式组件的完整功能。

对于开发者来说,使用类式组件或者函数式组件来开发功能实际上都是无所谓,谁好用就用谁。但设计者为了实现函数式组件可以说是绞尽脑汁。至于设计出来的东西好不好用另说。但函数式组件的这条路是一定要走下去的。

还有一个促使hooks诞生的原因是类式组件存在一些缺点。例如类式不好对功能进行拆分。当然hooks本身是否存在别的缺点我们另说。class概念难以理解以及this的指向问题处理对于初学者来说都是比较麻烦的。

1.hooks是完全可选的,你不用hooks,用类式组件也是完全没有问题的。
2.hooks是100%向后兼容的。hook不包含任何破坏性改动。

3.hooks的代码比类组件相对少一些。

组件插槽

react没有slot这样的节点概念,因为他不需要。

通过props.children实现插槽效果

通过props.children就可以直接实现插槽的效果。

  <body><div id="root"></div><script type="text/babel">class NavBar extends React.Component {state = { isHot: true };render() {const { children } = this.props;return (<div className="content"><div className="left">{children[0]}</div><div className="center">{children[1]}</div><div className="right">{children[2]}</div></div>);}changeWeather = () => {this.setState({ isHot: !this.state.isHot });};}NavBar.propTypes = {name: PropTypes.string.isRequired,age: PropTypes.number,speak: PropTypes.func,};NavBar.defaultProps = {name: "Tom",age: 19,};class Main extends React.Component {render() {return (<NavBar><div>我是左边内容</div><div>我是中间内容</div><div>我是右边内容</div></NavBar>);}}ReactDOM.render(<Main />, document.getElementById("root"));</script></body>

是可以实现插槽效果的。
在这里插入图片描述
但是,如果只有一个元素的时候,children并不是一个数组,而是一个元素对象,这时候通过数组下标取元素就不对了,虽然控制台并不会报错。这是react就是这样设计的,不要问为什么。 存在弊端。

            <NavBar><div>我是左边内容</div>{/*<div>我是中间内容</div><div>我是右边内容</div>*/}</NavBar>

在这里插入图片描述
props.children方式存在的问题:
1.children一会是数组一会是对象,容易出错。
2.children是通过下标来直接取元素的,可读性非常的差,容易取错。

通过props参数实现插槽效果

直接通过参数把元素内容传过去,是不是非常的神奇?
再react里面,直接在props里面传参数就可以实现插槽的效果。参数名称随便取。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="https://unpkg.com/react@18/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script><!-- Don't use this in production: --><script src="https://unpkg.com/@babel/standalone/babel.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script><style>.content {display: flex;/* align-items: center;justify-content: space-around; */height: 50px;.left {width: 100px;background: pink;}.center {width: 100%;background: lightblue;}.right {width: 100px;background: lightgreen;}}</style></head><body><div id="root"></div><script type="text/babel">class NavBar extends React.Component {render() {const { leftSlot, centerSlot, rightSlot } = this.props;console.log(this.props);return (<div className="content"><div className="left">{leftSlot}</div><div className="center">{centerSlot}</div><div className="right">{rightSlot}</div></div>);}}class Main extends React.Component {render() {return (<NavBarleftSlot={<div>我是左边内容</div>}rightSlot={<div>我是右边内容</div>}centerSlot={<div>我是中间内容</div>}/>);}}ReactDOM.render(<Main />, document.getElementById("root"));</script></body>
</html>

受控组件和非受控组件

受控组件本质上就是表单组件,在react中表单数据交由state管理。这是react自己创造的一个概念。
非受控组件在react中也是指表单组件,只是数据不是state控制的,用户提供直接操作dom来管理表单数据。几乎是不会用的,只是相当于受控组件的一个概念。

受控组件

下面的代码有一个非常重要的特点是,在设置了value的值后,浏览器的输入框是不能再输入内容的,也不能修改内容。这是被react作用到的。正常的html设置了input的value内容还是可以编辑的。

还有一个小细节是label的绑定id的属性是htmlFor,原来是for,这是因为for是关键字。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {render() {return (<div><label htmlFor="name">姓名:<input id="name" value="Tom" /></label></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

在这里插入图片描述
给input添加onChange事件,这就是受控组件了。我们在state中绑定了value值,这就是react的受控组件的概念了。实际就是给input组件实现了数据绑定。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {username: "Tom",};handleChange(e) {console.log(e.target.value);this.setState({username: e.target.value,});}render() {return (<div><label htmlFor="name">姓名:<inputid="name"value={this.state.username}onChange={(e) => this.handleChange(e)}/></label></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

表单提交行为和受控组件

下面的代码就是传统表单的提交方式。 这种方式现在已经没人用了。下面的代码在提交表单的时候会刷新页面。

              <form action="/adduser"><label htmlFor="name">姓名:<inputid="name"value={this.state.username}onChange={(e) => this.handleChange(e)}/></label><button type="submit">提交</button></form>

react的表单提交方式:
下面的代码先是取消了action属性。通过拦截onSubmit方法来实现表单的提交。核心的逻辑就是handleSubmit方法。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {username: "Tom",};handleSubmit(event) {console.log(event);//1.阻止默认行为event.preventDefault();//2.获取控件数据内容console.log(this.state.username);//3.通过axios提交给服务器}handleChange(event) {console.log(event.target.value);this.setState({username: event.target.value,});}render() {return (<div><form onSubmit={(e) => this.handleSubmit(e)}><label htmlFor="name">姓名:<inputid="name"value={this.state.username}onChange={(e) => this.handleChange(e)}/></label><button type="submit">提交</button></form></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

多个受控组件使用同一个函数处理

其实就是一个处理技巧。通过event.target.name来动态获取属性名,当然,需要我们指定元素name属性的值。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {username: "Tom",password: "123",};handleSubmit(event) {console.log(event);//1.阻止默认行为event.preventDefault();//2.获取控件数据内容console.log(this.state.username, this.state.password);//3.通过axios提交给服务器}handleChange(event) {console.log(event.target.name);this.setState({[event.target.name]: event.target.value,});}render() {return (<div><form onSubmit={(e) => this.handleSubmit(e)}><label htmlFor="name">姓名:<inputid="name"name="username"value={this.state.username}onChange={(e) => this.handleChange(e)}/></label><label htmlFor="name">密码:<inputid="password"type="password"name="password"value={this.state.password}onChange={(e) => this.handleChange(e)}/></label><button type="submit">提交</button></form></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

单个checkbox和多个checkbox

单个checkbox

我们实现一个登录页面的代码,有用户名密码和同意协议。需要注意的是,checkbox使用的属性是checked不是value。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {username: "Tom",password: "123",isAgree: false,};handleSubmit(event) {console.log(event);//1.阻止默认行为event.preventDefault();//2.获取控件数据内容const { username, password, isAgree } = this.state;console.log(username, password, isAgree);//3.通过axios提交给服务器}handleChange(event) {console.log(event.target.name);this.setState({[event.target.name]: event.target.value,});}handleCheckedChange(event) {this.setState({isAgree: event.target.checked,});}render() {return (<div><form onSubmit={(e) => this.handleSubmit(e)}><label htmlFor="name">姓名:<inputid="name"name="username"value={this.state.username}onChange={(e) => this.handleChange(e)}/></label><br /><label htmlFor="name">密码:<inputid="password"type="password"name="password"value={this.state.password}onChange={(e) => this.handleChange(e)}/></label><br /><label htmlFor="isAgree"><inputid="isAgree"type="checkbox"checked={this.state.isAgree}onChange={(e) => this.handleCheckedChange(e)}/>用户协议</label><button type="submit">提交</button></form></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>
多个checkbox

多个checkbox主要也是表单的一些选项,这是非常常见的。需要注意的是。我们需要定义一个数组来管理多个checkbox的状态。

  <body><div id="root"></div><script type="text/babel">class MyComponent extends React.Component {state = {username: "Tom",password: "123",isAgree: false,hobbies: [{ name: "唱", value: "sing", checked: false },{ name: "跳", value: "dance", checked: false },{ name: "rap", value: "rap", checked: false },],};handleSubmit(event) {console.log(event);//1.阻止默认行为event.preventDefault();//2.获取控件数据内容const { username, password, isAgree, hobbies } = this.state;const selectedHobbies = hobbies.filter((item) => item.checked).map((item) => item.value);console.log(username, password, isAgree);console.log(selectedHobbies);//3.通过axios提交给服务器}handleChange(event) {console.log(event.target.name);this.setState({[event.target.name]: event.target.value,});}handleCheckedChange(event) {this.setState({isAgree: event.target.checked,});}handleHobbiesChange(event, index) {const hobbies = [...this.state.hobbies];hobbies[index].checked = event.target.checked;this.setState({hobbies,});}render() {const { username, password, isAgree, hobbies } = this.state;return (<div><form onSubmit={(e) => this.handleSubmit(e)}><label htmlFor="name">姓名:<inputid="name"name="username"value={username}onChange={(e) => this.handleChange(e)}/></label><br /><label htmlFor="password">密码:<inputid="password"type="password"name="password"value={password}onChange={(e) => this.handleChange(e)}/></label><br />{hobbies.map((item, index) => {return (<label htmlFor={item.value} key={index}><inputid={item.value}type="checkbox"checked={item.checked}onChange={(e) => this.handleHobbiesChange(e, index)}/>{item.name}</label>);})}<br /><label htmlFor="isAgree"><inputid="isAgree"type="checkbox"checked={isAgree}onChange={(e) => this.handleCheckedChange(e)}/>用户协议</label><br /><button type="submit">提交</button></form></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

在这里插入图片描述

高阶函数和高阶组件

高阶函数回顾

什么是高阶函数。一个函数接收一个函数作为参数或者返回一个函数,那个这个函数就是高阶函数。

高阶组件(High Order-Component HOC)

这是react官方给的定义。
官方定义: 高阶组件是参数为组件,返回值为新组件的函数。
注意: 虽然叫高阶组件,但实际上是函数。
接收一个组件作为他的参数。

高阶组件简单例子

高阶组件的主要意义就是对传入的组件进行拦截,这样,我们就可以对传入的组件做一些额外操作了。
下面的代码就是一个简单的高阶组件,虽然没有什么实际意义。

  <body><div id="root"></div><script type="text/babel">//   import { PureComponent } from "react";class FooComponent extends React.PureComponent {render() {return <div></div>;}}function enhanceComponent(OldComponent) {class NewComponent {render() {return <OldComponent name="commonName" />;}}return NewComponent;}ReactDOM.render(<FooComponent />, document.getElementById("root"));const newComponent = enhanceComponent(FooComponent);console.log(newComponent);</script></body>

高阶组件的应用场景一:props

下面的代码可以给每个组件注入一个userInfo到各自的props里面。我们需要把自身的props也传进去。

还是有点用的,如果你需要给某些组件插入一些测试数据的话。

  <body><div id="root"></div><script type="text/babel">//   import { PureComponent } from "react";class MyComponent extends React.PureComponent {render() {return (<div><Home /><Profile /><Find /></div>);}}const Home = enhanceComponent(function (props) {return <div>Home {props.name}</div>;});const Profile = enhanceComponent(function (props) {return <div>Profile {props.name}</div>;});const Find = enhanceComponent(function Find(props) {return <div>Find {props.name}</div>;});function enhanceComponent(OldComponent) {class NewComponent extends React.PureComponent {state = {userInfo: {name: "Tom",age: 10,},};render() {return <OldComponent {...this.props} {...this.state.userInfo} />;}}return NewComponent;}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

高阶组件的应用场景二:真实应用场景 Context

我们在使用Context的时候,需要写类似下面的代码,这段代码看起来非常的繁琐,而且每次都需要这样写非常的恶心。通过高阶函数可以转变成即简单又优雅的实现方式。

  <body><div id="root"></div><script type="text/babel">const ThemeContext = React.createContext();class MyComponent extends React.PureComponent {render() {return (<div><ThemeContext.Provider value={{ color: "red", size: 30 }}><Product /></ThemeContext.Provider></div>);}}class Product extends React.PureComponent {render() {return (<div><ThemeContext.Consumer>{(value) => {return <div>{value.size}</div>;}}</ThemeContext.Consumer></div>);}}ReactDOM.render(<MyComponent />, document.getElementById("root"));</script></body>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/476060.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【FPGA开发】ZYNQ中PS与PL交互操作总结、原理浅析、仿真操作

文章目录 PL与PS交互综述交互端口性能&特点&#xff08;选择方案的凭据&#xff09;GPIO-AXI_GPDMA-DMACHP-AXI_HPACP-AXI_ACP 数据交互实验GP通过BRAMPS为主机&#xff0c;读写BRAMPL作为主机&#xff0c;读写BRAM DMA方式交互 PL与PS交互综述 网络上关于PS PL交互的教程…

【论文笔记】Large Brain Model (LaBraM, ICLR 2024)

Code: https://github.com/935963004/LaBraM Data: 无 目录 AbstractIntroductionMethodNeural tokenizer training&#xff1a;Pre-training LaBraM&#xff1a; ResultsExperimental setup&#xff1a;Pre-training result&#xff1a;Comparison with SOTA&#xff1a;Pre-t…

推荐几个 VSCode 流程图工具

Visual Studio Code&#xff08;简称VSCode&#xff09;是一个由微软开发的免费、开源的代码编辑器。 VSCode 发布于 2015 年&#xff0c;而且很快就成为开发者社区中广受欢迎的开发工具。 VSCode 可用于 Windows、macOS 和 Linux 等操作系统。 VSCode 拥有一个庞大的扩展市…

2024信创数据库TOP30之达梦DM8

近年来&#xff0c;中国信创产业快速崛起&#xff0c;其中数据库作为基础软件的重要组成部分&#xff0c;发挥了至关重要的作用。近日&#xff0c;由DBC联合CIW/CIS共同发布的“2024信创数据库TOP30”榜单正式揭晓&#xff0c;汇聚了国内顶尖的数据库企业及产品&#xff0c;成为…

将网站地址改成https地址需要哪些材料

HTTPS&#xff08;安全超文本传输协议&#xff09;是HTTP协议的扩展。它大大降低了个人数据&#xff08;用户名、密码、银行卡号等&#xff09;被拦截的风险&#xff0c;还有助于防止加载网站时的内容替换&#xff0c;包括广告替换。 在发送数据之前&#xff0c;信息会使用SSL…

RPC安全可靠的异常重试

当调用方调用服务提供方&#xff0c;由于网络抖动导致的请求失败&#xff0c;这个请求调用方希望执行成功。 调用方应该如何操作&#xff1f;catch异常再发起一次调用&#xff1f;显然不够优雅。这时可以考虑使用RPC框架的重试机制。 RPC框架的重试机制 RPC重试机制&#xff1…

【c++丨STL】priority_queue(优先级队列)的使用与模拟实现

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C、STL 目录 前言 一、priority_queue简介 二、priority_queue的使用 构造函数(constructor) empty size top push和pop swap 仿函数的使用 三、prio…

【数据结构】【线性表】【练习】删除链表倒数第n个结点

目录 申明 题目 分析题目信息 解题思路 代码解析 技巧解析&#xff1a;创建虚拟头结点 时间复杂度分析 思考&#xff1a;能否只用一趟扫描实现&#xff1f; 双指针 双指针解题思路 代码解析 申明 该题源自力扣题库19&#xff0c;文章内容&#xff08;代码&#xff0c…

Ubuntu20.04升级glibc升级及降级的心路历程

想使用pip安装Isaac Sim&#xff0c;无奈此方法只支持 GLIBC>2.34 。使用的是Ubuntu20.04&#xff0c;使用 ldd --version 查看GLIBC版本&#xff0c;如果版本低于 2.34 则需要升级GLIBC&#xff0c;基于此开始了长达一天的尝试。 请注意&#xff0c;升级GLIBC是一个危险操作…

uniapp实现开发遇到过的问题(持续更新中....)

1. 在ios模拟器上会出现底部留白的情况 解决方案&#xff1a; 在manifest.json文件&#xff0c;找到开源码视图配置&#xff0c;添加如下&#xff1a; "app-plus" : {"safearea":{"bottom":{"offset" : "none" // 底部安…

Electron开发构建工具electron-vite(alex8088)添加VueDevTools(VitePlugin)

零、介绍 本文章的electron-vite指的是这个项目&#x1f449;electron-vite仓库&#xff0c;electron-vite网站 本文章的VueDevTools指的是VueDevTools的Vite插件版&#x1f449;https://devtools.vuejs.org/guide/vite-plugin 一、有一个用electron-vite创建的项目 略 二、…

机器学习基础05_随机森林线性回归

一、随机森林 机器学习中有一种大类叫集成学习&#xff08;Ensemble Learning&#xff09;&#xff0c;集成学习的基本思想就是将多个分类器组合&#xff0c;从而实现一个预测效果更好的集成分类器。集成算法大致可以分为&#xff1a;Bagging&#xff0c;Boosting 和 Stacking…

Linux驱动开发(9):pinctrl子系统和gpio子系统--led实验

在前面章节&#xff0c;我们有过使用寄存器去编写字符设备的经历了。这种直接在驱动代码中&#xff0c; 通过寄存器映射来对外设进行使用的编程方式&#xff0c;从驱动开发者的角度可以说是灾难。 因为每当芯片的寄存器发生了改动&#xff0c;那么底层的驱动几乎得重写。 那么…

23种设计模式速记法

前言 在软件开发的过程中&#xff0c;设计模式作为解决常见问题的通用模板&#xff0c;一直是开发者的重要工具。尤其是在面临复杂系统架构和需求变化时&#xff0c;设计模式不仅能够提升代码的可复用性和扩展性&#xff0c;还能大大提高团队之间的协作效率。然而&#xff0c;…

IntelliJ+SpringBoot项目实战(十二)--设计项目多模块依赖关系和跨模块调用服务和接口

在非微服务的项目中&#xff0c;一个应用里有多个子系统&#xff0c;例如在一个电商系中&#xff0c;有系统管理子系统、内容管理子系统和电商管理子系统&#xff0c;我们想实现这样的效果&#xff1a; &#xff08;1&#xff09;只需要启动一个SpringBoot应用&#xff0c;不需…

MACOS开发、使用常见问题汇总

MACOS常见问题 本文记录使用macos遇到的常见问题&#xff0c;后面会持续更新&#xff0c;觉得有用的可以收藏一下。 打不开xxx.app&#xff0c;因为它来自身份不明的开发者解决方法(开启任何来源) 打开终端&#xff08;Terminal&#xff09;程序 拷贝sudo spctl --master-di…

【实用数据】上市公司数字化转型双重差分准自然实验数据(2007-2022年)

测算方式&#xff1a; 参考《管理评论》丁相安&#xff08;2024&#xff09;老师研究的做法&#xff0c;企业分批逐步推动自身数字化转型是一个很好的准自然实验&#xff0c;这符合双重差分法的使用情境。 因此&#xff0c;本文使用多时点双重差分模型&#xff08;&#xff24…

PostgreSQL常用字符串函数与示例说明

文章目录 coalesce字符串位置(position strpos)字符串长度与大小写转换去掉空格(trim ltrim rtrim)字符串连接(concat)字符串替换简单替换(replace)替换指定位置长度(overlay)正则替换(regexp_replace) 字符串匹配字符串拆分split_part(拆分数组取指定位置的值)string_to_array…

一次需升级系统的wxpython安装(macOS M1)

WARNING: The scripts libdoc, rebot and robot are installed in /Users/用户名/Library/Python/3.8/bin which is not on PATH. 背景&#xff1a;想在macos安装Robot Framework &#xff0c;显示pip3不是最新&#xff0c;更新pip3后显示不在PATH上 参看博主文章末尾 MAC系统…

细说STM32单片机DMA中断收发RTC实时时间并改善其鲁棒性的另一种方法

目录 一、工程配置 二、软件代码 1、软件代码 2、usart.h 3、usart.c 4、rtc.c 三、运行与调试 1、合规的指令 2、proBuffer[0]不是 ‘#’ 或proBuffer[4]不是 ‘;’ 3、指令长度小于5 4、proBuffer[2]或proBuffer[3]至少一个不是数字 5、; 位于proBuffer…