一、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