React学习笔记及心得体会(含函数组件和类组件的对比)

一、React项目创建

1.引入第三方插件创建React项目
  • 安装第三方插件
npm i react react-dom
  • 页面中引入react react-dom
<script src="./node_modules/react/umd/react.development.js">
<script src="./node_modules/react-dom/umd/react-dom.development.js">
  • 创建React元素
// 创建元素节点
// 1.元素名称
// 2.元素属性 传递的是个对象
// 3.元素内容
let title = React.createElement('li',null,'hellow react');
  • 渲染到页面
// 渲染到页面
ReactDom.render(title,root);
2.脚手架创建React项目
  • 1.创建项目
npm5.2+  (npm版本5.2以上)
npx create-react-app my-project(项目名)

npm5.2-  (npm版本5.2以下)
npm i create-react-app -g
create-react-app my-project(项目名)
  • 2.启动项目
npm start  /  yarn start

二、React目录结构

  • public文件夹
public -> 存放静态资源
  • src文件夹
src -> 存放源码
src/index.js  入口文件
  • gitignore
填写git提交代码时忽略的文件/文件夹
  • package.json
相关依赖下载信息
  • README.md
开发文档

打开隐藏的webpack配置

npm run eject  /  yarn run eject
  • 隐藏的config文件夹
webpack配置
  • scripts
环境配置
build.js  生产环境
start.js  开发环境
test.js   测试环境

三、React基础语法

JSX不是标准的ECMAScript语法,它是ECMAScript的语法拓展
需要使用babel编译处理后,才能在浏览器环境中使用
create-react-app脚手架中已经默认有该配置,无需手动配置
编译JSX语法的包: @bable/prest-react

React元素的属性名使用驼峰命名法
特殊属性名: class->className   for->htmlFor    tabindex->tabIndex
如果没有子节点的React元素可以用/>来结束
推荐: 使用小括号包裹JSX,从而避免JS中自动插入分号报错
JSX
  • 虚拟DOM
// 第一种
import React from 'react'
import ReactDOM from 'react-dom'

const mydiv = React.createElement('div',null,'我是一个div');

ReactDOM.render(
	mydiv,
    document.getElementById('root')
)

// 第二种
import React from 'react'
import ReactDOM from 'react-dom'


ReactDOM.render(
	<div>我是一个div</div>,
    document.getElementById('root')
)
  • 模板语法
import React from 'react'
import ReactDOM from 'react-dom'

let msg = 'hello react'

ReactDOM.render(
	<div>
    	{msg}
    </div>
    document.getElementById('root')
)
  • 条件渲染
//第一种
import React from 'react'
import ReactDOM from 'react-dom'

let isloading = false
function isShowUI() {
	if(isloading) {
        return <div>loading...</div>
    }
    else {
        return <div>done</div>
    }
}

ReactDOM.render(
	<div>
    	{isShowUI()}
    </div>
    document.getElementById('root')
)
//第二种 (三元运算符)
import React from 'react'
import ReactDOM from 'react-dom'

let isloading = false

ReactDOM.render(
	<div>
    	{isloading?'loading...':'done'}
    </div>
    document.getElementById('root')
)
//第三种 (用于显示/隐藏的简洁写法)
import React from 'react'
import ReactDOM from 'react-dom'

let isloading = false

ReactDOM.render(
	<div>
    	{isloading&&'loading...'}
    </div>
    document.getElementById('root')
)
  • 列表渲染
import React from 'react'
import ReactDOM from 'react-dom'

let list = [
    {
        id:0,
        name:'fly'
    },
    {
        id:1,
        name:'sky'
    }
]

ReactDOM.render(
	<div>
    	{list.map(item=>{
			return <p key={item.id}>{item.id}----{item.name}</p>	    
		})}
    </div>
    document.getElementById('root')
)
  • 样式绑定
import React from 'react'
import ReactDOM from 'react-dom'
//import './index.css'
import './index.scss'  //使用scss
import classNames form 'classnames'

let isloading = false
let objStyle = { color:'red' }

ReactDOM.render(
	<div>
    	<h1 className="title">样式绑定-className</h1>
    	<h2 style={{ color:'red' }}>样式绑定-style</h2>
		<h2 style={objStyle}>样式绑定-style(外部对象写法)</h2>
		<h2 className={classNames({ title:true, active:true })}>样式绑定-多个类名classnames</h2>
    </div>
    document.getElementById('root')
)
.title{
    color:red;
}
.active{
    text-decoration:line-through;
}
************************************************************************************************
一个标签多个类名时使用classnames   需要 yarn add classnames 安装classnames插件
然后import classNames form 'classnames' 引入
************************************************************************************************

四、React组件

1.函数组件
1.使用js的函数创建组件
2.函数名称必须以大写字母开头
3.函数组件必须有返回值,表示该组件的结构
4.如果返回值为null,表示不渲染任何内容
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// 以函数方式定义组件(函数组件第一种写法)
// 1.函数名首字母大写
// 2.函数需要return 
/* function App(){
	// return null;
	// return <div> <span>app</span> </div>;
	// 加上()更方便查看 可以不加
	return (<div> <span>app</span> </div>);
} */

// 函数组件第二种写法
/* const App = () => {
	return (
		<div>
			<span>
				app
			</span>
		</div>
	)
} */

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
2.类组件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// 以class方式定义组件
class App extends React.Component {
	render() {
		return (
			<div>
				<span>
					app
				</span>
				<MyComponent />
			</div>
		)
	}
}


ReactDOM.render(
  <App />,
  document.getElementById('root')
);
3.自定义组件
在src下自定义一个js文件(名称为自定义组件名),可以不在src下,再新建一个components文件夹专门存放组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPtoHzhc-1634049746098)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210731163417042.png)]

  • 自定义函数组件
MyComponent.js

import React from 'react'

export default function MyComponent(){
	return (
		<div>
			以箭头?函数方式定义的组件,一般只是用于做渲染---/---组件有生命周期,而以函数方式定义的组件没有生命周期,可以提高渲染速度
		</div>
	);
}
首先import引入自定义的组件
1.直接在ReactDOM.render中使用自定义组件
2.在当前文件定义的(函数/类)组件引入自定义组件
index.js

import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent.js' //自定义的组件
import './index.css';

class App extends React.Component {
	render() {
		return (
			<div>
				<span>
					app
				</span>
				<MyComponent />
			</div>
		)
	}
}


ReactDOM.render(
  <App />,
	// <MyComponent />,
  document.getElementById('root')
);
  • 自定义类组件
import React,{ Component } from 'react'

export default class ClassComponent extends Component{
	render() {
		return (
			<div>
				<RCF />
				<RCC />
			</div>
		);
	}
}
  • 组件套组件
新建一个compoents文件夹存放组件,在src下自定义的组件文件中引入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NAZi5QK-1634049746100)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210731170858344.png)]

import React,{ Component } from 'react'
import RCF from './compoents/RCF.js' //引入自定义的组件
import RCC from './compoents/RCC.js' //引入自定义的组件

export default class ClassComponent extends Component{
	render() {
		return (
			<div>
				<RCF />
				<RCC />
			</div>
		);
	}
}
index.js文件引入这个组件即可实现套娃
4.受控组件
HTML中的表单元素是可输入的,也就是有自己的可变状态
而React中可变状态通常保存在state中,并且只能通过 setState()方法来修改
React讲state与表单元素value绑定在一起,有state的值来控制表单元素的值
受控组件:值受到react控制的表单元素

受控组件又是有状态组件
import React,{ Component } from 'react'

export default class ShouKong extends Component{
	// constructor() { 
	// 	super()
	// 	this.state = {
	// 		value:'构造函数里的value'
	// 	}
	// }
	
	state = {
		value:'hello react'
	}
	
	render() {
		const { value } = this.state;
		return (
			<div>
				<div>{value}</div>
				<input value={value} onChange={this.handleChangeValue} />
			</div>
		);
	}
	
	handleChangeValue = (e) => {
		this.setState({
			value:e.target.value
		})
		
		// 第二种写法  暂无说明其用途
		// this.setState(()=>{
		// 	return {
		// 		value:e.target.value
		// 	}
		// })
	}
	

}
5.非受控组件(含ref使用)
说明:借助于ref,使用元素DOM方式获取表单元素值
ref的作用:获取DOM或者组件

使用步骤:
调用 React.createRef()方法创建ref对象
将创建好的ref对象添加到文本框中
通过ref对象获取到文本框的值
  • 第一种方式 直接通过ref与this.refs
import React,{ Component } from 'react'

export default class FeiShouKong extends Component{
	
	render() {
		return (
			<div>
				<input ref="inputRef"/>
				<button onClick={this.handleClick}>点我</button>
			</div>
		);
	}
	
	handleClick = () => {
		console.log(this.refs.inputRef);  // 拿到ref对象
		console.log(this.refs.inputRef.value); //  拿到ref对象 input输入框的值
	}
	

}
  • 第二种方式 createRef结合构造函数
import React,{ Component, createRef } from 'react'

export default class FeiShouKong extends Component{
	constructor() { 
		super()
		this.inputRef = createRef()
	}
		
	render() {
		return (
			<div>
				<input ref={this.inputRef}/>
				<button onClick={this.handleClick}>点我</button>
			</div>
		);
	}
	
	handleClick = () => {
		console.log(this.inputRef.current); // 拿到当前的ref对象
		console.log(this.inputRef.current.value); // 拿到ref对象 input输入框的值
	}
	

}
两种方式的区别
1.数据绑定 第一种数据绑定为 ref="inputRef"  第二种为 ref={this.inputRef}
2.构造函数 第一种不用到构造函数 第二种需要先在react中import引入createRef方法 在构造函数中this.inputRef = createRef() 初始化一个变量通过createRef()方法
3.获取ref对象  第一种 this.refs.inputRef   第二种 this.inputRef.current
4.第一种为16.3以下老版本使用 第二种为新版本

五、React事件处理

React事件绑定语法接近js原生语法
语法: on+事件名称=事件处理函数, ps:onclick = function(){}
注意:React事件采用驼峰命名法
onClick的写法与原生js一致 后面写的是{this.handleClick.bind(this)}
this.handleClick是在react对象里的函数 之所以还要.bind(this)是因为需要取到state(在react对象)里的值

函数的层级与render函数一致,类似于wxapp

state类似于vue(wxapp)的data  取值和改变类似于wxapp
四种事件绑定可以取到state里值的方法
  • 第一种:通过bind改变this指向react对象
  • 第二种:通过使用箭头函数指向上下文对象(react对象 ) 这里函数调用需要加()
  • 第三种:在构造函数中使用bind改变this的指向(第三种需结合constructor构造函数一起使用)
  • 第四种:以箭头函数的方式定义函数
  • 建议使用第三种/第四种
import React,{ Component } from 'react'

export default class ClassComponent extends Component{
	constructor() { // 结合第三种方式一起使用
		super()
		this.state = {
			msg:'构造函数里的msg'
		}
		this.handleClick = this.handleClick.bind(this);
	}
	
	state = {
		msg:'hello react'
	}
	
	render() {
		return (
			<div>class类的方式定义的组件(类组件,有生命周期)<br/>
				{/*this.handleClick是在react对象里的函数 之所以还要.bind(this)是因为需要取到state(在react对象)里的值*/}
				{/* 第一种:通过bind改变this指向react对象 */}
				<button onClick={this.handleClick.bind(this)}>点我</button>
				{/* 第二种:通过使用箭头函数指向上下文对象(react对象 ) 这里函数调用需要加()*/}
				<button onClick={() => this.handleClick()}>点我</button>
				{/* 第三种:在构造函数中使用bind改变this的指向 */}
				<button onClick={this.handleClick}>点我</button>
				{/* 第四种:以箭头函数的方式定义函数 */}
				<button onClick={this.handleClickArr}>点我</button>
			</div>
		);
	}
	
	handleClick(){
		// console.log("是我呀");
		console.log(this.state.msg);
	}
	
	handleClickArr = () => {
		console.log('箭头函数'+this.state.msg);
	}
}
  • 拓展

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OogBQH1U-1634049746101)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210801170619256.png)]

六、this.setState实现双向绑定

  • 有状态组件/无状态组件
函数组件又叫做无状态组件,类组件又叫做有状态组件
状态(state)即数据
函数组件没有自己的状态,只负责数据展示
类组件有自己的状态,负责更新UI,让页面动起来

人话:有state(data数据)就是有状态,不然就是无状态,函数组件是无状态,类组件是有状态组件
react中jsx取state中的值  ps:<div>{this.state.msg}</div>
this.setState是一个是一个异步请求实现双向绑定,所以要使用一下两种方式来实现
// 第一种异步 用setState方法里的回调函数实现异步调用
// 第二种异步 使用async await实现异步 推荐使用第二种
import React,{ Component } from 'react'

export default class StateBind extends Component{
	// 暂时不知构造函数里的state与正常state的区别
	// constructor() { 
	// 	super()
	// 	this.state = {
	// 		msg:'构造函数里的msg'
	// 	}
	// }
	
	state = {
		msg:'hello react'
	}
	
	render() {
		return (
			<div>
			  <div>{this.state.msg}</div>
				<input value={this.state.msg} onChange={this.handleChangeTwo} />
			</div>
		);
	}
	
	
	handleChange = (e) => {
		// 与wxapp里获取input的值别无二致
		// console.log(e.target.value);
		// 因为这个是异步函数不能确定与下方的打印谁先执行
		this.setState({
			msg:e.target.value
		});
		console.log(this.state.msg);
	}
	
	// 这里写箭头函数是为了this指向的缘故
	handleChangeOne = (e) => {
		// 第一种异步 用setState方法里的回调函数实现异步调用
		this.setState({
			msg:e.target.value
		},()=>{
			console.log(this.state.msg)
		});
	}
	
	handleChangeTwo = async (e) => {
		// 第二种异步 使用async await实现异步 推荐使用第二种
		await this.setState({
			msg:e.target.value
		});
		console.log(this.state.msg)
	}
	
}

七、React组件传值

React组件进阶:
	1.能够使用props接收数据
	2.能够实现父子组件之间的通讯
	3.能够实现兄弟组件之间的通讯
	4.能够给组件添加props校验	
组件通讯:因为组件是独立且封闭的单元,默认情况下只能使用组件自己的数据,多组件共享数据,需要打破组件的独立封闭性
1.组件的props
组件是封闭的,接收外部数据应该通过props实现 
props的作用:接收传递给组件的数据
传递数据:给组件标签添加属性
	例:<Hello name="jack" age={19} />
组件内部无法控制也无法修改外部传入的props
特点:
	可以给组件传任意类型的数据
	props是只读属性,不能对值进行修改
	注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取props,其他的地方是可以拿到的
import React,{ Component } from 'react'
import Rcc from './components/Rcc.js'
import Rfc from './components/Rfc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<Rfc title={'标题'} />
				<Rcc title={'标题'} />
			</div>
		);
	}
}
  • 函数组件(props)
函数组件通过参数props接收数据

<Rfc title={'标题'} />
Rfc组件 函数组件
import React from 'react'

export default function Rfc(props){
	console.log(props);
	return (
		<div>
			无状态组件 --- {props.title}
		</div>
	);
}

// 无状态组件 --- 标题
  • 类组件(this.props)
<Rcc title={'标题'} />
类组件通过this.props接收数据
也可以通过state来保存props传过来的值 this.state.xxx
但是需要再constructor传入props参数获取props再存入this.state中,不然直接在state中使用会报错提示props不存在???*****************************暂不了解,有时间记得去查阅学习一下**************************************
Rcc组件 类组件
import React,{ Component } from 'react'

export default class Rcc extends Component{
	// 类组件可以通过构造函数来获取,操作props
	constructor(props) {
		super(props);
		this.state = {
			title: props.title
		}
	};
	
	// 会报错提示title找不到
	// state = {
	// 	title: this.props.title
	// }
	
	render() {
		return (
			<div>
				有状态组件 --- {this.props.title}
				{this.state.title}
			</div>
		);
	}
}

// 有状态组件 --- 标题标题
  • 函数组件(props.children)
函数组件通过参数props.children获取组件下所有子元素
<Rfc>标题<h1>哈哈哈</h1></Rfc>
Rfc组件 函数组件
import React from 'react'

export default function Rfc(props){
	console.log(props);
	return (
		<div>
			{/* props.children拿到组件下的所有子元素 */}
			无状态组件 --- {props.children}
		</div>
	);
}

// 无状态组件 --- 标题 
// 哈哈哈
  • 类组件(this.props.children)
类组件通过this.props.children获取组件下所有子元素
<Rcc>标题<h2>嘿嘿嘿</h2></Rcc>
Rcc组件 类组件
import React,{ Component } from 'react'

export default class Rcc extends Component{
	// 类组件可以通过构造函数来获取,操作props
	constructor(props) {
		super(props);
		this.state = {
			title: props.title
		}
	};
	
	render() {
		return (
			<div>
				有状态组件 --- {this.props.children}
			</div>
		);
	}
}

// 有状态组件 --- 标题
// 嘿嘿嘿
2.子传父
react子传父的原理与vue相似,更接近原生
父组件渲染子组件时添加属性传入函数 <SonToFatherRcc getData={this.getData} />
getData(data){
		console.log(data)
	}


子组件获取props中的函数并进行调用传入参数,父组件获取该参数,既是子传父
<button onClick={this.handelClick}>类组件子传父</button>
handelClick = () => {
		this.props.getData('类组件子传父的数据')
	}
import React,{ Component } from 'react'

import SonToFatherRcc from './components/SonToFatherRcc.js'
import SonToFatherRfc from './components/SonToFatherRfc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<SonToFatherRcc getData={this.getData} />
				<SonToFatherRfc getData={this.getData} />
			</div>
		);
	}
	
	getData(data){
		console.log(data)
	}
}
  • 类组件
import React,{ Component } from 'react'

export default class SonToFatherRcc extends Component{
	render() {
		return (
			<div>
				有状态组件 <button onClick={this.handelClick}>类组件子传父</button>
			</div>
		);
	}
	
	// 要使用箭头函数,不然不可直接使用this,因为函数里的this指向undefined
	handelClick = () => {
		this.props.getData('类组件子传父的数据')
	}
}

// 控制台打印:类组件子传父的数据
  • 函数组件
import React from 'react'

export default function SonToFatherRfc(props){
	console.log(props);
	const handelClick = () => {
		props.getData('函数组件子传父的数据')
	}
	return (
		<div>
			无状态组件 --- <button onClick={handelClick}>函数组件子传父</button>
		</div>
	);
}
3.兄弟传值
1.将共享状态(数据)提升到最近的公共父组件中,由公共父组件管理这个状态
2.这个称为状态提升
3.公共父组件职责: 提供共享状态 提供操作共享状态的方法
4.要通讯的子组件只需要通过props接收状态或操作状态的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3ouYY39-1634049746102)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210922150132162.png)]

本质上是子传父结合父传子
import React,{ Component } from 'react'

import BrotherRcc1 from './components/BrotherRcc1.js'
import BrotherRcc2 from './components/BrotherRcc2.js'
import BrotherRfc1 from './components/BrotherRfc1.js'
import BrotherRfc2 from './components/BrotherRfc2.js'

export default class APP extends Component{	
	state = {
		count:0
	}
	render() {
		return (
			<div>
				<BrotherRcc1 count={this.state.count}/>
				<BrotherRcc2 addCount={this.addCount}/>
				<BrotherRfc1 count={this.state.count}/>
				<BrotherRfc2 addCount={this.addCount}/>
			</div>
		);
	}
	
	addCount = (data) => {
		// this.setState({
		// 	count: this.state.count + data
		// })
		/* 用这种方法,先取到最新的state状态再进行相关变化,
		可以防止因为定时器等时间延迟的影响出现状态改变有异的情况 */
		this.setState((nextState)=>{
			return{
				count:nextState.count + data
			}
		})
	}
}
  • 类组件
BrotherRcc1组件 展示渲染,父传子的那一环

import React,{ Component } from 'react'

export default class BrotherRcc1 extends Component{
	// 类组件可以通过构造函数来获取props
	constructor(props) {
		super(props);
		this.state = {
			title: props.title
		}
	};
	
	
	render() {
		return (
			<div>
					有状态count:{this.props.count}
			</div>
		);
	}
}
BrotherRcc2组件 传值操作 子传父的那一环

import React,{ Component } from 'react'

export default class BrotherRcc2 extends Component{
	// 类组件可以通过构造函数来获取props
	constructor(props) {
		super(props);
		this.state = {
			title: props.title
		}
	};
	
	
	render() {
		return (
			<div>
				<button onClick={this.add}>有状态添加</button>
			</div>
		);
	}
	add = () => {
		this.props.addCount(1);
	}
}
  • 函数组件
BrotherRfc1组件 展示渲染,父传子的那一环

import React from 'react'

export default function BrotherRfc1(props){
	return (
		<div>
			无状态组件Count:{props.count}
		</div>
	);
}
BrotherRfc2组件 传值操作 子传父的那一环

import React from 'react'

export default function BrotherRfc2(props){
	const add = () => {
		props.addCount(1);
	}
	return (
		<div>
			<button onClick={add}>无状态组件添加</button>
		</div>
	);
	
}
父组件有一个共享状态 
其中一个兄弟组件父传子获取该共享状态
另外一个兄弟组件子传父改变该共享状态
4.Context传值
如果出现层级比较多的情况下(例如:爷爷传递给孙子),我们会使用Context来进行传递 
作用:跨组件传递数据

Context提供了两个组件: Provider 和 Consumer
Provider组件: 用来提供数据
Consumer组件: 用来接收(消费)数据
const { Provider, Consumer } = React.createContext()

使用Provider组件作为父节点
<Provider>
	<div>
		<Child1 />
	</div>
</Provider>

设置value属性,表示要传递的数据
<Provider value="pink">

哪一层想要接收数据,就用Consumer进行包裹,在里面回调函数中的参数就是传递过来的值
<Consumer>
	{data = > <span>data参数表示接收到的数据 -- {data}</span>}
</Consumer>
  • 类组件
import React,{ Component } from 'react'

// Provider 生产  Consumer 消费
const { Provider, Consumer } = React.createContext();

// 爷爷组件
class ContextRcc extends Component{
	state = {
		obj:{
			name:'旭旭宝宝',
			age:'25'
		}
	}
	
	render() {
		return (
			<Provider value={this.state.obj}>
				<div>
					<h2>爷辈有状态组件</h2>
					<Parent />
				</div>
			</Provider>
		);
	}
}

class Parent extends Component{
	render() {
		return (
				<div>
					<h2>父辈有状态组件</h2>
					<Grandson />
				</div>
		);
	}
}

class Grandson extends Component{
	render() {
		return (
				<div>
					<h2>孙辈有状态组件</h2>
					<Consumer>
						{
							data => <span>data就是从Provider里获取到的value值 -- {data.name+data.age}</span>
						}
					</Consumer>
				</div>
		);
	}
}

export default ContextRcc
用<Provider value=xxxx></Provider>组件包裹爷爷组件
在孙子组件用<Consumer>{data => <span>{data}</span>}</Consumer>接收Provider的value值即可,无需包裹孙子组件
  • 函数组件(未使用useContext)
import React, {createContext} from 'react'

const FirstContext = React.createContext();

function ContextRfc(){
	return (
		<FirstContext.Provider value={18}>
			<div>
				<h2>爷辈无状态组件</h2>
				<Parent />
			</div>
		</FirstContext.Provider>
	);
}

function Parent(){
	return (
		<div>
			<h2>父辈无状态组件</h2>
			<Grandson />
		</div>
	);
}

function Grandson(){
	return (
		<div>
			<h2>孙辈无状态组件</h2>
			<FirstContext.Consumer>
				{
					data => <span>data就是从Provider里获取到的value值 -- {data}</span>
				}
			</FirstContext.Consumer>
		</div>
	);
}

export default ContextRfc
1.相较于类组件使用const { Provider, Consumer } = React.createContext();引入Provider组件和Consumer组件
2.函数组件使用import React, {createContext} from 'react' 引入
3.且在使用之前const FirstContext = React.createContext(); 定义一个上下文对象
4.在传统类组件的Provider和Consumer加上这个变量对象
<FirstContext.Provider />  <FirstContext.Consumer />
5.其余与类组件使用方式无异
  • 函数组件(未使用useContext)
import React, {createContext,useContext} from 'react'

const FirstContext = React.createContext();

function ContextRfc(){
	return (
		<FirstContext.Provider value={18}>
			<div>
				<h2>爷辈无状态组件</h2>
				<Parent />
			</div>
		</FirstContext.Provider>
	);
}

function Parent(){
	return (
		<div>
			<h2>父辈无状态组件</h2>
			<Grandson />
		</div>
	);
}

function Grandson(){
	const data = useContext(FirstContext)
	return (
		<div>
			<h2>孙辈无状态组件</h2>
			<span>data就是从Provider里获取到的value值 -- {data}</span>
		</div>
	);
}

export default ContextRfc
与不使用useContext的函数组件相比
1.在引入时会多引入useContext import React, {createContext,useContext} from 'react'
2.在Provider组件的使用上无区别
3.在Consumer组件的使用上,如果使用const data = useContext(xxxContext)接收了Provider的value则不需要使用Consumer了
5.组件抽离实现Context传值
当不是单个文件内实现Context传值时,也就是将爷辈,父辈,孙辈三个组件抽离为单独的文件
  • 类组件
App.js

import React,{ Component } from 'react'
import GrandfatherRcc from './divisionContextRcc/GrandfatherRcc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<GrandfatherRcc />
			</div>
		);
	}
}
GrandfatherRcc.js

import React,{ Component } from 'react'

import Parent from './Parent.js'
import {Provider} from '../utils/contextUtil.js' // 引入封装好的工具组件里的Provider,确保Context一致

// 这样子肯定拿不到爷辈使用Context传过来的数据 因为Provider和Consumer并不是同一个React.createContext()
// const {Provider} = React.createContext();

export default class GrandfatherRcc extends Component{
	render() {
		return (
			<Provider value={18}>
				<div>
					<h2>爷辈有状态组件</h2>
					<Parent />
				</div>
			</Provider>
		);
	}
}
Parent.js

import React,{ Component } from 'react'

import Grandson from './Grandson.js'
export default class Parent extends Component{
	render() {
		return (
			<div>
				<h2>父辈有状态组件</h2>
				<Grandson />
			</div>
		);
	}
}
import React,{ Component } from 'react'

import {Consumer} from '../utils/contextUtil.js' // 引入封装好的工具组件里的Provider,确保Context一致

// 这样子肯定拿不到爷辈使用Context传过来的数据 因为Provider和Consumer并不是同一个React.createContext()
// const {Consumer} = React.createContext();

export default class Grandson extends Component{
	render() {
		return (
			<div>
				<h2>孙辈有状态组件</h2>
				<Consumer>
					{
						data => <span>这是爷辈传下来的数据 -- {data}</span>
					}
				</Consumer>
			</div>
		);
	}
}
Context工具

import React from 'react'

const { Provider, Consumer } = React.createContext()

// 如果是通过export default暴露则是以对象形式
export {
	Provider,
	Consumer
}
如果是直接分别在爷辈组件和孙辈组件中引入Provider,Consumer
const { Provider, Consumer } = React.createContext()
这样的话因为不在一个文件中引入的,其实React.createContext()对象并不是同一个,所以孙辈肯定拿不到爷辈的数据

所以需要先封装一个Context的工具js文件
在这个文件中const { Provider, Consumer } = React.createContext() 然后export暴露出来
之后再分别在父辈组件和孙辈组件中import引入,这样就能确保是同一个Context,孙辈也就能拿到爷辈的数据了
  • 函数组件(未使用useContext)
App.js

import React,{ Component } from 'react'
import GrandfatherRfc from './divisionContextRcc/GrandfatherRfc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<GrandfatherRcc />
			</div>
		);
	}
}
Context工具

import React, {createContext} from 'react'

const FirstContext = React.createContext();

// 如果是通过export default暴露则是以对象形式
export {
	FirstContext
}
GrandfatherRfc.js

import React from 'react'

import Parent from './Parent.js'
import {FirstContext} from '../utils/contextRfcUtil.js'

export default function GrandfatherRfc(){
	return (
		<FirstContext.Provider value={25}>
			<div>
				<h2>爷辈无状态组件</h2>
				<Parent />
			</div>
		</FirstContext.Provider>
	);
}
Parent.js

import React from 'react'

import Grandson from './Grandson.js'

export default function Parent(){
	return (
		<div>
			<h2>父辈无状态组件</h2>
			<Grandson />
		</div>
	);
}
Grandson.js

import React, {createContext,useContext} from 'react'

import {FirstContext} from '../utils/contextRfcUtil.js'
export default function Grandson(){
	return (
		<div>
			<h2>孙辈无状态组件</h2>
			<FirstContext.Consumer>
				{
					data => <span>data就是从Provider里获取到的value值 -- {data}</span>
				}
			</FirstContext.Consumer>
		</div>
	);
}
  • 函数组件(使用useContext)
Grandson.js 只有这个有所改变 相对于未使用useContext

import React, {createContext,useContext} from 'react'

import {FirstContext} from '../utils/contextRfcUtil.js'
export default function Grandson(){
	const data = useContext(FirstContext)
	return (
		<div>
			<h2>孙辈无状态组件</h2>
			<span>data就是从Provider里获取到的value值 -- {data}</span>
		</div>
	);
}
函数组件抽离实现Context传值中间的工具组件只需要使用createContext创建一个上下文对象即可
通过这个中间的工具组件可以创建多个上下文对象,只需要在爷孙组件各自引入对应的上下文对象即可
6.props校验
Props的children属性
	表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
	children属性与普通的props一样,值可以使任意值(文本、react元素、组件、甚至是函数)
	
function Hello(props) {
	return (
		<div>
			组件的子节点:{props.children}
		</div>
	)
}
<Hello>我是子节点</Hello>
Props检验
	对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装者需要什么样的数据
	如果传入的数据不对,可能会导致报错
	关键问题:组件的使用者不知道需要传递什么样的数据
	props检验:允许在创建组件的时候,指定props的类型、格式

App.propTypes = {
	colors:propTypes.array
}
	
	作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

  • 类组件
App.js

import React,{ Component } from 'react'
import PropsCheckRcc from './components/PropsCheckRcc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<PropsCheckRcc title={2}>
					<div>我是有状态的内容<span>哈哈</span></div>
				</PropsCheckRcc>
			</div>
		);
	}
}

PropsCheckRcc.js

import React,{ Component } from 'react'

import PropTypes from 'prop-types'

export default class PropsCheckRcc extends Component{
	static propTypes = {
		title: PropTypes.string,
		children: PropTypes.object
	}
	render() {
		return (
			<div>
				有状态组件 --- {this.props.title}
				<br/>
				有状态组件 --- {this.props.children}
			</div>
		);
	}
}
引入PropTypes
定义props校验
static propTypes = {
		title: PropTypes.string,
		children: PropTypes.object
	}
注意区别两个不同的propsTypes  PropsTypes 前者为内置的  后者为引入的
  • 函数组件
App.js

import React,{ Component } from 'react'
import PropsCheckRfc from './components/PropsCheckRfc.js'

export default class APP extends Component{	
	render() {
		return (
			<div>
				<PropsCheckRfc title={2}>
					<div>我是无状态的内容<span>哈哈</span></div>
				</PropsCheckRfc>
			</div>
		);
	}
}


PropsCheckRfc.js

import React from 'react'
import propTypes from 'prop-types'

export default function PropsCheckRfc(props){
	return (
		<div>
			无状态组件 --- {props.title}
			<br/>
			{/* props.children拿到组件下的所有子元素 */}
			无状态组件 --- {props.children}
		</div>
	);
}

PropsCheckRfc.propTypes = {
	title:propTypes.string,
    children:propTypes.object
}
在封装好接收props的组件中import引入propTypes import propTypes from 'prop-types'
PropsCheckRfc.propTypes = {
	title:propTypes.string,
    children:propTypes.object
}
通过重写组件的propTypes对象来达到编写props校验的效果
其中如果是校验children,如果是有div等标签结构而非纯字符串则为object类型

不通过props校验时会在控制台提示警告,而非错误

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-39tZsBcb-1634049746103)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210923180441680.png)]

7.props默认值
场景:分页组件 -> 每页显示条数,
与props校验写法类似 只是无需引入第三方插件  校验为propTypes 默认值为defaultProps
与props检验都可以提高代码的健壮性
App.js

import React,{ Component } from 'react'
import DefaultPropsRcc from './components/DefaultPropsRcc.js'
import DefaultPropsRfc from './components/DefaultPropsRfc.js'

export default class APP extends Component{	
	state = {
		count:0
	}
	render() {
		return (
			<div>
				<DefaultPropsRcc title='我是传过来的有状态组件标题'>我是传过来的有状态组件子元素</DefaultPropsRcc>
				<DefaultPropsRfc title='我是传过来的无状态组件标题'>我是传过来的无状态组件子元素</DefaultPropsRfc>
			</div>
		);
	}
}
  • 类组件
import React,{ Component } from 'react'


export default class DefaultPropsRcc extends Component{
	static defaultProps = {
		title:'我是默认的有状态组件标题',
		children:'我是默认的有状态组件子元素'
	}
	render() {
		return (
			<div>
				有状态组件 --- {this.props.title}
				<br/>
				有状态组件 --- {this.props.children}
			</div>
		);
	}
}
  • 函数组件
import React from 'react'


export default function DefaultPropsRfc(props){
	return (
		<div>
			无状态组件 --- {props.title}
			<br/>
			{/* props.children拿到组件下的所有子元素 */}
			无状态组件 --- {props.children}
		</div>
	);
}

DefaultPropsRfc.defaultProps = {
	title:'我是默认的无状态组件标题',
	children:'我是默认的无状态组件子元素'
}
当props没有传值时,展示defaultProps的默认值,当传值的时候会展示props传的值
props传值  优先级>  defaultProps默认值

defaultProps默认值和propTypes校验结合使用可以让你组件代码更加的健壮

八、React生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9epVysdl-1634049746103)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210924105813858.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WL5HtJyy-1634049746104)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210924105743453.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjpUdI4v-1634049746104)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210924110737484.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3FaMptu-1634049746105)(C:\Users\qifen\AppData\Roaming\Typora\typora-user-images\image-20210924110847276.png)]

找找别的教程看看

九、React网络请求(axios)

有时间再去看看吧

十、render-props模式

React组件复用概述
思考:如果两个组件中的部分功能相似或相同,该如何处理?
处理方式:复用相似的功能
复用什么?
	state
	操作state的方法
两种方式:
	render props模式
	高阶组件 (HOC)
注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式
思路分析
思路:将要复用的state和操作state的方法封装到一个组件中
如何拿到该组件中复用的state
	在使用组件中,添加一个值为函数的prop,通过函数参数来获取
	<Mouse render={(mouse) => {}}/>
如何渲染到任意的UI
	使用该函数的返回值作为要渲染的UI内容

day01 myself

// 以下在ReactDOM.render 中渲染时的变量不是双向的数据流,而是单向数据流?所以只有第一次有用,后面不会变化?
// setInterval(()=>{
// 	isloading = !isloading;
// 	console.log(isloading);
// },100)
<div>{isloading ? 'loading...':'done'}</div>

react的条件渲染只有dom树的改变,重绘  不像vue有v-show  wxapp有wx-hidden

react也需要有一个根元素包裹,vue可以用<template></template>只用以包裹不实际渲染的标签 而在react中为<></>

react中没有列表渲染的指令(vue v-for) 只能用高阶函数(例如map函数来遍历达到列表渲染)
<div>{list.map(item=>{
			return <p key={item.id}>{item.id} ---->  {item.name}</p>
		})}</div>
vue中的:key="xx" 在react中表现为key={xx}

四种事件绑定可以取到state里值的方法
+ 第一种:通过bind改变this指向react对象
+ 第二种:通过使用箭头函数指向上下文对象(react对象 ) 这里函数调用需要加()
+ 第三种:在构造函数中使用bind改变this的指向(第三种需结合constructor构造函数一起使用)
+ 第四种:以箭头函数的方式定义函数
+ 建议使用第三种/第四种

react中jsx取state中的值  ps:<div>{this.state.msg}</div>
this.setState是一个是一个异步请求实现双向绑定,所以要使用一下两种方式来实现 类似于wxapp
// 第一种异步 用setState方法里的回调函数实现异步调用
// 第二种异步 使用async await实现异步 推荐使用第二种

受控组件与非受控组件(用于ref)
两种方式的区别
1.数据绑定 第一种数据绑定为 ref="inputRef"  第二种为 ref={this.inputRef}
2.构造函数 第一种不用到构造函数 第二种需要先在react中import引入createRef方法 在构造函数中this.inputRef = createRef() 初始化一个变量通过createRef()方法
3.获取ref对象  第一种 this.refs.inputRef   第二种 this.inputRef.current
4.第一种为16.3以下老版本使用 第二种为新版本

day02 myself

vue自定义组件需要定义,注册,渲染
而React只需要定义,渲染,无需注册

组件内部无法控制也无法修改外部传入的props


React Hook "useContext" cannot be called at the top level. React Hooks must be called in a React function 
component or a custom React Hook function  react-hooks/rules-of-hooks

*****************Context传值中Provider的value不仅能传数据也可以传方法,对象********************

useContext等hooks只能用在React函数组件中

React万物皆组件

Typo in static class property declaration  react/no-typos
组件.propTypes一定要是首字母小写,其他可以写成大写的PropTypes
***********************************propTypes 而不是 PropTypes  首字母并不大写**********************


当props没有传值时,展示defaultProps的默认值,当传值的时候会展示props传的值
props传值  优先级>  defaultProps默认值

杂记

1.export和export default的区别
2.base64格式的图片
data:image/png;base64,base64编码的png图片数据
<bind name="lang" value="@io.choerodon.mybatis.helper.LanguageHelper@language()"/>
<bind name="tenantId" value="@io.choerodon.core.oauth.DetailsHelper@getUserDetails().getTenantId()"/>
select
	cast(lzpet.PRODUCTION_EQUIPMENT_TABLE_ID as char) PRODUCTION_EQUIPMENT_TABLE_ID,
	cast(lzpet .PRODUCTION_EQUIPMENT_ID as char) PRODUCTION_EQUIPMENT_ID,
	lzpet .PRODUCTION_EQUIPMENT_CODE,
	cast(lzpet .EQUIPMENT_ID as char) EQUIPMENT_ID,
	lzpet .EQUIPMENT_CODE ,
	lzpet .EQUIPMENT_NAME ,
    cast(le.PROD_LINE_ID as char) PROD_LINE_ID,
    le.PROD_LINE_CODE 
from
	hlos_wms.lwms_zhenyu_production_equipment_table lzpet 
    left join hlos_mds.lmds_equipment le on le.equipment_id=lzpet .EQUIPMENT_ID
	where 
	lzpet .TENANT_ID =155
	<if test="equipmentId!=null">
		and lzpet .EQUIPMENT_ID =#{equipmentId}
	</if>
	<if test="equipmentCode!=null">
		and lzpet .EQUIPMENT_CODE =#{equipmentCode}
	</if>
<if test="productionEquipmentCode!=null">
		and lzpet .PRODUCTION_EQUIPMENT_CODE =#{productionEquipmentCode}
	</if>
	<if test="equipmentName!=null">
		<bind name="equipmentName" value="'%' + equipmentName + '%'"/>
		and lzpet .EQUIPMENT_NAME like #{equipmentName}
	</if>


LMDS.EQUIPMENT_PRODUCT  LWMS.ZY_EQUIPMENT_TABLE
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值