React基础
react中创建组件可以选用JSX语法
环境要求
node > 8.1 npm > 5.6
环境搭建
npx create-react-app 名字
创建完项目之后,整理项目目录
index.js为入口文件
react文件引入
react框架可以做
- web端程序 — web网站
- app程序 — react-native框架
import React from 'react' --- 必须的
import ReactDom from 'react - dom' ---
react中组件的书写模式jsx格式
jsx是JavaScript的书写格式,类似模板引擎
jsx是对JavaScript语法的扩展
说是html又不是html,说是字符串又不是字符串
jsx原理实现过程
原生:
<body>
<div id="root">
<button id="btn_zan">
<span id="btn_text">点赞</span>
<span>?</span>
</button>
</div>
</body>
<script>
// 原生js实现jsx
let btn_z = document.querySelector("#btn_zan");
let btn_t = document.querySelector("#btn_text");
console.log(btn_z, btn_t);
// 原生js代码
let isClick = false;
btn_z.onclick = function () {
isClick = !isClick;
if (isClick) {
btn_t.innerHTML = "取消";
} else {
btn_t.innerHTML = "点赞";
}
};
</script>
- 简单的处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script>
// 原生js实现jsx
let root = document.querySelector("#root");
// 1. 抽离一个按钮组件
class button {
render() {
return `<button id="btn_zan">
<span id="btn_text">点赞</span>
<span>?</span>
</button>`;
}
}
// 2. 将抽取的按钮组件放回到root中
root.innerHTML = new button().render();
let btn_z = document.querySelector("#btn_zan");
let btn_t = document.querySelector("#btn_text");
console.log(btn_z, btn_t);
// 原生js代码
let isClick = false;
btn_z.onclick = function () {
isClick = !isClick;
if (isClick) {
btn_t.innerHTML = "取消";
} else {
btn_t.innerHTML = "点赞";
}
};
</script>
</html>
- 组件化添加事件 — 事件提到组件内部书写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script>
// 原生js实现jsx
let root = document.querySelector("#root");
// 3. 抽离一个creatElement方法
function creatElement(str) {
let parent = document.querySelector("#root");
parent.innerHTML = str;
// 返回父级
return parent;
}
// 1. 抽离一个按钮组件
class button {
render() {
// el就是根
this.el = creatElement(`<button id="btn_zan">
<span id="btn_text">点赞</span>
<span>?</span>
</button>`);
// 4.添加事件
this.el.addEventListener("click", function () {
console.log(111);
});
return this.el;
}
}
// 2. 将抽取的按钮组件放回到root中
root.appendChild(new button().render());
</script>
</html>
- 完善事件功能,添加状态变量
React基础语法
组件的写法
- 函数组件 — 无状态组件
- 类组件 — 有状态组件
区别:
- 函数组件用来渲染UI界面
- 类组件用来写复杂的逻辑代码
类组件的写法
写法一
// 声明class组件
import React from 'react'
// 组件名首字母必须大写
class IndexPage extends React.Component{
constructor(){
super();
}
render(){
return(
<div>组件</div>
)
}
}
export default IndexPage
写法二
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return ( );
}
}
export default 名字;
函数组件的写法
//创建纯函数组件
function My({dataindex}){
console.log(dataindex);
return (
<div>
我的组件
</div>
)
}
// function My(props){
// console.log(props);
// return (
// <div>
// 我的组件
// </div>
// )
// }
export default My;
组件的挂载
import './App.css';
//引入组件--同步引入挂载
import IndexPage from '../view/Index-page'
import MyPage from '../view/My-page'
import Cart from '../view/Cart'
function App() {
return (
<div className="App">
默认主模板
{/*挂载的标签使用方式
<html></html>
<html/>
*/}
<IndexPage name='张三'></IndexPage>
<MyPage dataindex="123"/>
<Cart></Cart>
</div>
);
}
export default App;
组件中的super关键字
如果在声明中没有使用super会报以下错误
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
super() 是执行父类的构造
类组件中
constructor(props) {
console.log("props是:", props); // props是: {}
super();//在使用this之前一定super()
//类组件中的状态值
this.state={
msg:'xiaoxi'
}
}
play() {
console.log(this.state, this.props); // {num: 0} {}
}
类组件中的state是状态值,props是接收组件传值 — 属性配置
start与props的区别?
- start是属性的状态值,props是组件的属性配置–传值
- 函数组件中的props与类组件中的props作用相同
- state是可以修改的,props是只读的
props按照解构的写法书写(没理解)
函数props解构
function My({dataindex}){
console.log(dataindex);
return (
<div>
我的组件
</div>
)
}
类中写法
constructor({name}) {
super();//在使用this之前一定super()
//类组件中的状态值
this.state={
msg:'xiaoxi'
}
}
类组件中状态值的使用
render() {
//直接解构
let { list } = this.state;
return (
<div>
购物车
{/* 绑定变量 */}
{this.state.num}
{this.state.str}
{/* {}内部可以书写三元运算符 */}
{this.state.ishas ? '取消' : '点赞'}
{this.state.ishas ? <h2>取消</h2> : <h1>点赞</h1>}
{/* 数组类型变量 */}
{
this.state.list
}
{
list.map((item,index)=>{
return (
<h1>
{item}
</h1>
)
})
}
</div>
);
}
循环渲染报元素key
{
list.map((item,index)=>{
return (
<h1 key={index}>
{item}
</h1>
)
})
}
类组件中定义细节
this.state = {
num: 0,
str: 'abc',
ishas: false,
list: [1, 2, 3, 4],
stu: [
{
name: '小花',
hobby: ['篮球']
},
{
name: '小米',
hobby: ['篮球', '乒乓球']
}
],
un: <h1>测试</h1>
}
绑定
render() {
//直接解构
let { list, stu } = this.state;
return (
<div>
购物车
{/* 绑定变量 */}
{this.state.num}
{this.state.str}
{/* {}内部可以书写三元运算符 */}
{this.state.ishas ? '取消' : '点赞'}
{this.state.ishas ? <h2>取消</h2> : <h1>点赞</h1>}
{/* 数组类型变量 */}
{
this.state.list
}
{
list.map((item, index) => {
return (
<h1 key={index}>
{item}
</h1>
)
})
}
{
stu.map((item, index) => {
return (
<div key={index}>
{item.name}
----
{
item.hobby.map((it, i) => {
return <span key={i}>{it}</span>
})
}
</div>
)
})
}
{
this.state.un
}
</div>
);
}
标签上的属性绑定
{/* 标签上的属性绑定
分为静态和动态绑定*/}
<button title="随便">按钮</button>
<button title={this.state.str}>按钮</button>
{/* 元素类绑定 class
class不能直接使用
class----className
for htmlFor
*/}
{/* <div className='cart'></div> */}
<div className={CartCss.cart}></div>
<div style={this.state.style}></div>
<div style={obj}></div>
<div className={this.state.ishas?'':''}></div>
如果是按照模块方式加载css文件
文件名字命名:
import { Component } from "react";
// 方法一:直接引入
// import "../assets/css/index.module.css";
// 方法二:声明引入
import cl from "../assets/css/xx.module.css";
// 方法四:直接定义css
let obj = {
color: "yellow",
};
class name extends Component {
constructor(props) {
super(props);
this.state = {
name: "王五",
// 方法三:直接在state中声明,绑定成行内样式
sty: {
color: "red",
},
};
}
render() {
return (
<div>
{/* 静态绑定 */}
<input type="text" value="张三" />
{/* 动态绑定 */}
<input type="text" value={this.state.name} />
{/* 方法一:直接引入 */}
{/* <input type="text" value="张三" className="bbb" /> */}
{/* 方法二:声明引入 */}
<input type="text" value="张三" className={cl.bbb} />
{/* 方法三:直接在state中声明,绑定成行内样式 */}
<input type="text" value="张三" style={this.state.sty} />
{/* 方法四:直接定义css */}
<input type="text" value="张三" style={obj} />
</div>
);
}
}
export default name;
修改state
使用API setState()
在reactjs库管控区域属于异步,非管控位置属于同步
setState方法在修改之后 直接获取无法获取最新的值—setState是异步的想要获取更新之后的数据 setState 它的第二个参数为一个匿名函数回调执行修改操作之后返回最新值
setState第二个参数为啥是一个函数
对象写法
//参数使用对象格式
this.setState({
msg:'你好'
});
console.log(this.state.msg);//输出的不是修改
//确定是异步
对象类型写法
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = { num: 0 };
}
play() {
this.setState(
{
num: this.state.num + 1,
},
() => {
console.log(this.state.num);
}
);
console.log(this.state.num);
}
render() {
return (
<div>
{this.state.num}
<button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
</div>
);
}
}
export default 名字;
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = { num: 0 };
}
play() {
this.setState(
{
num: this.state.num + 1,
},
() => {
console.log(this.state.num); // 1
}
);
// setState是异步,实质上是与原数据进行比较替换,
// 故两次this.state.num + 1均传了1回去
this.setState(
{
num: this.state.num + 1,
},
() => {
console.log(this.state.num); // 1
}
);
console.log(this.state.num); // 0
}
render() {
return (
<div>
{this.state.num}
<button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
</div>
);
}
}
export default 名字;
参数写成函数类型写法
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = { num: 0 };
}
play() {
// 使用函数的写法,就会使用另一个语句,函数会排队故num: xxx.num + 1,可以累计
this.setState(
(xxx) => {
console.log(xxx); // {num:0}
return {
num: xxx.num + 1,
};
},
() => {
console.log(this.state.num); // 1
}
);
this.setState(
(xxx) => {
console.log(xxx); // {num:0}
return {
num: xxx.num + 1,
};
},
() => {
console.log(this.state.num); // 2
}
);
}
render() {
return (
<div>
{this.state.num}
<button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
</div>
);
}
}
export default 名字;
非管控位置指的就是被js原生组件包括的区域
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = { num: 0 };
}
play() {
setTimeout(() => {
this.setState({
num: 3,
});
// setState被转化为同步函数,故:输出值为3 而不是0
console.log(this.state.num); // 3
});
}
render() {
return (
<div>
{this.state.num}
<button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
</div>
);
}
}
export default 名字;
props验证
import { Component } from "react";
//开始props验证
import Propstypes from "prop-types"; // 所有的数据类新
//props属性约束是类的静态字段 (类名称点)
//函数组件外部可以通过函数名称点类型约束
//函数内部不能写 static
class 名字 extends Component {
constructor(props) {
super(props);
this.state = {
text: "props验证",
};
}
// 方法二:内部定义(static:静态字段关键字)
static propTypes = {
name: Propstypes.string,
};
render() {
console.log(this.props);
return <div>{this.state.text}</div>;
}
}
// 第一种是在类或者函数外部直接定义,类型约束(注意:propTypes指的是propTypes对象、类的静态字段)
名字.propTypes = {
name: Propstypes.string,
};
// 定义props的默认值
名字.defaultProps = {
name: "你叫啥名字?",
};
export default 名字;
事件代理
import { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = {
top: this.addHandle.bind(this),
};
}
// 方案一
handle() {
console.log(this);
}
// 方案二
head = () => {
console.log(this);
};
// 方案三
addHandle() {
console.log(this);
}
render() {
return (
<div>
{/* 方案一 */}
<button onClick={this.handle.bind(this)}>按钮</button>
{/* 方案二 */}
<button onClick={this.head}>按钮</button>
{/* 方案三 */}
<button onClick={this.state.top}>按钮</button>
</div>
);
}
}
export default 名字;
事件的执行参数
常用属性
addHandle(e){
//事件参数中常用属性
//坐标 PageX PageY ClientX ClientY
//触发的目标元素 e.target
//e.preventDefault() 阻止事件的默认行为
//e.stopPropagation() 阻止事件冒泡的
console.log(e.target);//可以直接操作原生js
console.log(this,e)
}
事件执行的时候传递参数
几种方案
//事件绑定传值 事件参数后移
sendMsg(args,args1,e) {
console.log('执行', args,args1,e,arguments);
}
{/* 事件传参 */}
{/* <button onClick={() => {
//基本的事件执行 调用方法传参
this.sendMsg(1);
}}>传参</button> */}
{/* <button onClick={this.sendMsg.bind(this,1,2)}>传参</button> */}
<button onClick={this.sendMsg}>传参</button>
react中的元素事件不是原生的js事件 类似事件 是元素的属性
理解执行机制:
react中存在单独的事件处理机制,类似委托,所有的react元素的类事件属性全部注册到document上通过addEventListener,存储所有的事件执行函数,在挂载完成阶段委托到当前元素 执行对应的事件处理函数。
先注册到document实现捕获,在到目标阶段,在找到当前触发元素,根据key去对应事键存储中找到对应的事件处理函数,在进行合成(把目标和事件处理函数合成到events对象),在进行批处理(执行)。
受控与非受控组件
受控组件
有React管理数据的组件叫受控组件
react推荐使用受控组件来书写表单数据
react中的数据是单项数据流*****
表单元素的value 不是原生js的value属性, defaultValue是原生js的value
受控组件中的value需要借助onChange事件来实现数据双向
受控组件自己的state状态需要由自己维护需要onChange事件实现双向
setName(e) {
//实现数据双向
let { target } = e;
let { user } = this.state;
user.name = target.value;//获取原生js的value属性值
this.setState({
user: user
});
}
render() {
let { user } = this.state;
return (
<div>
<form>
<ul>
<li>
<label htmlFor="userName">姓名:</label>
<input name='userName' onChange={this.setName.bind(this)} value={user.name} type="text" />
</li>
<li><button>提交</button></li>
</ul>
</form>
</div>
);
}
受控组件的使用
各种表单元素
最简单的表单
import React, { Component } from "react";
class 名字 extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
};
this.handle = this.handle.bind(this);
}
handle(e) {
let target = e.target;
console.log(target); // 元素本身 <input name="name" type="text" value="a">
this.setState({
name: target.value,
});
}
render() {
let { name } = this.state;
return (
<div>
<label htmlFor="name">姓名</label>
<input
name="name"
type="text"
value={name}
onChange={this.handle}
/>
</div>
);
}
}
export default 名字;
import React, { Component } from 'react';
class Index extends Component {
constructor(props) {
super(props);
this.state = {
// user: {
// name: '',
// age: 0
// }
name: '',
age: 0,
sex: '男',
hobby: [
{
name: '篮球',
isck: false
},
{
name: '足球',
isck: false
}
],
city: '',
txt: ''
}
this.handle = this.handle.bind(this);
}
setName(e) {
//实现数据双向
let { target } = e;
let { user } = this.state;
user.name = target.value;//获取原生js的value属性值
this.setState({
user: user,
});
}
setAge(e) {
//实现数据双向
let { target } = e;
let { user } = this.state;
user.age = target.value;//获取原生js的value属性值
this.setState({
user: user,
});
}
//优化使用字面量解析实现优化
handle(e) {
//获取目标元素
let target = e.target;
let name = target.name;//获取元素的名称 ---对应的就是变化的属性
switch (name) {
case 'hobby':
//获取对应的索引
let index = parseInt(target.getAttribute('data-index'));
let { hobby } = this.state;
hobby[index].isck = !hobby[index].isck;
this.setState({
hobby: hobby
});
break;
default:
this.setState({
[name]: target.value
});
}
}
//表单提交方法
subInfo(e) {
console.log(this.state);
//阻止事件默认行为
e.preventDefault();
}
render() {
let { name, age, sex, hobby, city, txt } = this.state;
return (
<div>
<form onSubmit={this.subInfo.bind(this)}>
<ul>
<li>
<label htmlFor="name">姓名:</label>
<input name='name' onChange={this.handle} value={name} type="text" />
</li>
<li>
<label htmlFor="age">年龄:</label>
<input name='age' onChange={this.handle} value={age} type="text" />
</li>
<li>
<label htmlFor="sex">性别:</label>
<input type="radio" checked={sex === '男'} value='男' onChange={this.handle} name='sex' />男
<input type="radio" checked={sex === '女'} value='女' onChange={this.handle} name='sex' />女
</li>
<li>
<label htmlFor="hobby">爱好:</label>
{
hobby.map((item, index) => {
return (
<div key={index}>
<input name='hobby' checked={item.isck} data-index={index} onChange={this.handle} type="checkbox" value={item.name} />{item.name}
</div>
)
})
}
</li>
<li>
<label htmlFor="city">城市:</label>
<select name='city' value={city} onChange={this.handle}>
<option value=''>--请选择--</option>
<option value='西安市'>--西安市--</option>
<option value='宝鸡市'>--宝鸡市--</option>
<option value='咸阳市'>--咸阳市--</option>
</select>
</li>
<li>
<label htmlFor="txt">备注:</label>
<textarea name='txt' onChange={this.handle} value={txt}></textarea>
</li>
<li><button>提交</button></li>
</ul>
</form>
</div>
);
}
}
export default Index;
非受控组件
-
类似使用原生js的代码操作数据,
表单元素的数据不维护
可以使用defaultValue绑定初始化
<form>
<ul>
<li>
{/* defaultValue 是原生js的value属性
value 是受控的
*/}
<input type="text" onChange={this.handle.bind(this)} defaultValue={this.state.name} />
</li>
</ul>
</form>
还可以操作获取DOM元素节点获取值
react16.8版本之前 ref属性可以直接写在元素之上
<li> <input type="text" ref="ipt" />//16.8 之前 </li>
现在react版本是17.0.2
import React, { Component, createRef } from "react";
// 引入函数子组件
import Son from "../component/Son";
// 引入类子组件
import Son1 from "../component/Son1";
class 非受控 extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
};
this.aaa = createRef();
this.bbb = createRef();
this.ccc = createRef();
}
handel(e) {
let target = e.target;
console.log(target.value);
}
subInfo(e) {
console.log(this.aaa);
console.log(this.bbb);
console.log(this.ccc);
e.preventDefault(); // 阻止默认操作
}
render() {
return (
<div>
{/* onsubmit 事件在表单提交时触发。 */}
<form action="" onSubmit={this.subInfo.bind(this)}>
<ul>
<li>
{/* 方法一 */}
<input
type="text"
onChange={this.handel.bind(this)}
defaultValue={this.state.name}
/>
</li>
<li>
{/* 方法二:使用ref */}
<input type="text" ref={this.aaa} />
</li>
<li>
<button>确定</button>
</li>
</ul>
</form>
{/* 子组件 */}
{/* 方法三:获得子组件内容 */}
<Son ref={this.bbb}></Son>
<Son1 ref={this.ccc}></Son1>
</div>
);
}
}
export default 非受控;
refApi
-
ref允许创建节点或者在render方法中创建react元素
ref用法:
- ref关联到html元素标签上时,current属性是原生的js对象,可以直接操作
- ref关联到子组件上(class组件),current属性代表当前组件的实例
- 在函数组件上不能直接使用ref获取,,,函数组件没有实例化
上面第二个方法的代码
创建接收子组件的实例对象
import React, { Component, createRef } from "react";
// 引入函数子组件
import Son from "../component/Son";
// 引入类子组件
import Son1 from "../component/Son1";
class 非受控 extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
};
this.aaa = createRef();
this.bbb = createRef();
this.ccc = createRef();
}
handel(e) {
let target = e.target;
console.log(target.value);
}
subInfo(e) {
console.log(this.aaa);
console.log(this.bbb);
console.log(this.ccc);
e.preventDefault(); // 阻止默认操作
}
render() {
return (
<div>
{/* onsubmit 事件在表单提交时触发。 */}
<form action="" onSubmit={this.subInfo.bind(this)}>
<ul>
<li>
{/* 方法一 */}
<input
type="text"
onChange={this.handel.bind(this)}
defaultValue={this.state.name}
/>
</li>
<li>
{/* 方法二:使用ref */}
<input type="text" ref={this.aaa} />
</li>
<li>
<button>确定</button>
</li>
</ul>
</form>
{/* 子组件 */}
{/* 方法三:获得子组件内容 */}
<Son ref={this.bbb}></Son>
<Son1 ref={this.ccc}></Son1>
</div>
);
}
}
export default 非受控;
// 函数组件
import { forwardRef } from "react";
let ReaderTable = forwardRef((props, ref) => {
return <div ref={ref}>子组件 -- 函数组件</div>;
});
export default ReaderTable;
// 类组件
import React, { Component } from "react";
class Dialog extends Component {
constructor(props) {
super(props);
this.state = {
name: "李四",
};
}
getData() {}
render() {
let { name } = this.state;
return <div>子组件--{name}</div>;
}
}
export default Dialog;
react组件间通信
组件上的props进行通信,props是只读的
父组件传递数据到子组件
父组件想要获取到整个子组件可以使用ref创建DOM节点
(class类组件可以直接获取 ref节点对象current属性直接当前组件)
(函数组件 不能直接使用ref 可以通过forwardRef转发ref到子组件的元素上)
父组件直接获取函数子组件
-
父 => 子
// 父元素 import React, { Component } from "react"; // 引入子组件 import Son1 from "../components/son1"; class 首页 extends Component { constructor(props) { super(props); this.state = { // 步骤一 text: [1, 2, 3, 4], }; } sendMSG() { alert("111"); } render() { let { text } = this.state; return ( <div> 我是首页 <br /> {/* 挂载子组件 */} {/* 步骤二 下面的parent属性传递的是整个父组件 相当于在子组件中通过parent获得整个父组件 */} <Son1 parent={this} son_text={text}></Son1> </div> ); } } export default 首页;
// 子元素 // 函数组件 // 引入数据验证模块 import propTypes from "prop-types"; // props是组件传参集合,只读的 // 步骤三 function name({ son_text, parent }) { // 获得整个父组件 console.log(parent); return ( <div> 我是你的子组件-1 <br /> {/* 步骤四 */} {son_text.map((item, index) => { return <p key={index}>父元素传来了:{item}</p>; })} {/* 子组件执行父组件方法 */} <button onClick={parent.sendMSG}>执行父组件方法</button> </div> ); } // 函数组件的约束只能在外部书写 // 类型的约束 name.propTypes = { son_text: propTypes.array, parent: propTypes.object, }; // 默认值的约束 name.defaultProps = { son_text: [], }; export default name;
- 子传父
// 父组件 // 步骤一:导出creatRef import React, { Component, createRef } from "react"; // 引入子组件 // import Son1 from "../components/son1"; import Son2 from "../components/son2"; class 首页 extends Component { constructor(props) { super(props); this.state = { text: [1, 2, 3, 4], }; // 步骤二:定义DOM节点对象 this.newDOM = createRef(); } // sendMSG() { // alert("111"); // } play() { console.log(this.newDOM); } render() { // let { text } = this.state; return ( <div> 我是首页 <br /> {/* 挂载子组件 */} {/* 下面的parent属性传递的是整个父组件 相当于在子组件中通过parent获得整个父组件 */} {/* 步骤三:父组件获得直接获取子组件 */} {/* <Son1 ref={this.newDOM} parent={this} son_text={text}></Son1> */} <Son2 ref={this.newDOM}></Son2> <button onClick={this.play.bind(this)}>获取子组件</button> </div> ); } } export default 首页;
// 子组件 // 步骤四 import { forwardRef } from "react"; let name = forwardRef((props, ref) => { return ( <div ref={ref}> 我是你的子组件-2 <br /> </div> ); }); export default name;
// 类组件 import React, { Component, ref } from "react"; class 子页 extends Component { constructor(props) { super(props); this.state = {}; } play2() { console.log("我在子组件上"); } render() { let { my_text } = this.props; return ( <div ref={ref}> 子组件-3 <br /> <ul> {my_text.map((item, index) => { return <li key={index}>我是子组件-3的:{item}</li>; })} </ul> </div> ); } } export default 子页;
组件的状态提升
假设一个父组件中存在两个子组件,一个子组件数据发生变化 ,另一个子组件同步修改,直接使用父子传值 props是只读的不能实现,
将子组件的状态统一提升到父组件中管理
// 父组件
// 组件的状态提升
import React, { Component } from "react";
import Child1 from "../components/child1";
import Child2 from "../components/child2";
class 我的 extends Component {
constructor(props) {
super(props);
this.state = {
num: 20,
};
}
my_update(n) {
console.log(n);
this.setState({
num: n,
});
}
render() {
return (
<div>
我的组件 <hr />
<Child1 onUpdate={this.my_update.bind(this)}></Child1>
<hr />
<Child2 my_num={this.state.num}></Child2>
</div>
);
}
}
export default 我的;
// 子组件1
import React, { Component } from "react";
class 子组件1 extends Component {
constructor(props) {
super(props);
this.state = {
num: 0,
};
}
play(e) {
let target = e.target;
let name = target.name;
this.setState({
[name]: target.value,
});
this.props.onUpdate(target.value);
}
render() {
return (
<div>
子组件1 <br />
{this.state.num} <br />
<input
name="num"
type="number"
value={this.state.num}
onChange={this.play.bind(this)}
/>
</div>
);
}
}
export default 子组件1;
import React, { Component } from "react";
class 子组件2 extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
let { my_num } = this.props;
console.log(my_num);
if (my_num < 0) {
return <div>结冰了</div>;
} else if (my_num >= 100) {
return <div>水沸腾了</div>;
} else {
return <div>烧水中</div>;
}
}
}
export default 子组件2;
非父子组件之间传值
- 事件总线
- redux状态管理
事件总线
// 创建str/utils.event.js文件配置事件总线
import events from 'events'
let eventEmitter = new events.EventEmitter()
export default eventEmitter
// 监听事件
import React, { Component } from "react";
import Event from "../utils/event";
class 子组件2 extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
let my_num = 20
console.log(my_num);
let But = (
<button
onClick={() => {
// 触发自定义事件
Event.emit("sendInfo", my_num);
}}>弹框</button>
);
return (
<div>
结冰了 <br />
{But}
</div>
);
}
}
export default 子组件2;
// 获得监听事件传出的值
import React, { Component } from "react";
import Event from "../utils/event";
class 获取事件总线 extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
Event.on("sendInfo", (params) => {
console.log(params);
});
return <div>获取事件总线</div>;
}
}
export default 获取事件总线;
react中的条件渲染
react中类似javascript中一样可以根据三元运算符和if条件动态渲染UI界面
在类组件中render进行判断(if 或者 ?:)
render() {
//直接写if或者三元控制渲染
let { isshow } = this.props;
if(isshow)
{
return (
<div>home另一个组件</div>
)
}
return (
<div>
home组件
</div>
);
}
定义元素变量的写法控制UI
render() {
//定义一个元素变量组件
let { isshow } = this.state;
let element ;
if(isshow){
element=<div>第一种情况</div>;
}
else{
element=<div>第二种情况</div>;
}
return (
<div>
home组件
{element}
</div>
)
}
条件+&&符操作UI
<div>
home组件
{element}
{this.state.arr.length > 0 && <h2>h2组件</h2>}
</div>
阻止react组件渲染
直接return null
react中组件的懒加载
封装一个组件懒加载的方法
//实现组件的懒加载
import React, { Component } from 'react';
//fn 接收 ()=>import('../view/Cart')
function AsyncComponent(fn) {
return class Async extends Component {
constructor(props) {
super(props);
this.state = {
Component: null
}
}
componentDidMount() {
//执行import() 返回的是promise
fn().then((res) => {
this.setState({ Component: res.default });
});
}
render() {
let { Component } = this.state;
return (
//{...this.props} props属性传递
Component ? <Component {...this.props}></Component> : null
);
}
}
}
export default AsyncComponent;
组件中使用
//引入懒加载模块
import AsyncCom from '../util/AsyncComponent'
let Cart = AsyncCom(() => import('../view/Cart'));
安装
cnpm i --save-dev react-loadable
使用
//引入第三方懒加载
import LoadAble from 'react-loadable'
function loading() {
return (
<div>正在加载...</div>
)
}
let Cart = LoadAble({
loader: () => import('../view/Cart'),
loading: loading
});
React的生命周期
一个组件从初始化==> 挂载完成 ==> 更新 ==> 卸载 这个过程叫组件的生命周期
组件首次初始化过程
class类组件首次挂载 执行的过程
代码:
import React, { Component } from 'react';
//class组件实例 初始化构造函数
class Index extends Component {
constructor(props) {
super(props);
this.state = {}
console.log('进入构造函数');
}
//组件挂载完成的生命周期
componentDidMount() {
console.log('组件挂载完成');
}
render() {
console.log('进入render');
return (
<div>
首页class组件
</div>
);
}
}
export default Index;
组件首次初始化执行constructor构造 ,再执行render(编译jsx格式为虚拟DOM,在转化为真实DOM,外加事件合成机制,再去root节点挂载),最后执行挂载完成的生命周期。
真实机制constructor调用之后换回执行一个方法获取默认的props参数,getDerivedStateFromProps()
:
组件更新过程
// 子组件
import React, { Component } from "react";
class 生命周期 extends Component {
constructor(props) {
super(props);
this.state = {
msg: "hello",
};
console.log("进入构造函数");
}
// 挂载之前
componentWillMount() {
console.log("挂载之前");
}
// 挂载完成
componentDidMount() {
console.log("挂载完成");
}
// 组件的props发生变化
componentWillReceiveProps(props) {
console.log("组件的props发生变化", { props });
}
// 组件发生变化前
shouldComponentUpdate(props, state) {
console.log(props, state);
if (state.msg !== this.state.msg || props.title !== this.props.title) {
return true;
}
return false;
}
// 组件发生变化完成
componentDidUpdate(props, state) {
console.log(props, state);
}
componentWillUnmount() {
console.log("卸载之前");
}
//事件方法
setData() {
this.setState({ msg: "你好" });
}
render() {
console.log("进入render");
return (
<div>
<p>{this.state.msg}</p>
<button onClick={this.setData.bind(this)}>更新数据</button>
</div>
);
}
}
export default 生命周期;
// 父组件
import './App.css';
import React, { Component } from 'react';
//引入组件--同步引入挂载
import 生命周期 from '../view/item'
class APP extends Component {
constructor(props) {
super(props);
this.state = {
name: '李四',
show: true
}
}
play() {
this.setState({
name: '张三'
})
}
gogo() {
this.setState({
show: !this.state.show
})
}
render() {
return (<div>
{/*挂载的标签使用方式
<html></html>
<html/>
*/}
{
this.state.show ? <生命周期 /> : null
}
<button onClick={this.play.bind(this)}>改变父组件的props</button>
<button onClick={this.gogo.bind(this)}>卸载子组件</button>
</div>);
}
}
export default APP;
getDefaultProps | 得到默认的props |
---|---|
getinitialState | 得到最初的state |
componentWillMount | 挂载之前 |
render | 渲染 |
componentDidMount | 挂载完成 |
componentWillReceiveProps | props发生变化之前 |
shouldComponentUpdate | 组件将要发生变化 |
componentDidUpdate | 组件发生变化 |
compontWillUnmount | 组件卸载前 |
组件的动态挂载
使用bool变量实现
{/* 下面方案可以实现组件的动态挂载 */}
{this.state.show ? <Index title={msg}></Index> : null}
数组方式
{/* 可以使用数组有值或者没值来确认组件动态挂载 */}
{
this.state.arr.map(() => {
return (
<Index title={msg}></Index>
)
})
}
常规写的周期存在警告 旧名字可以使用 ,也可以加前缀 UNSAFE_
react中的路由
github中文文档
https://v5.reactrouter.com/web/example/basic
根据文档操作
1.安装react-router-dom
cnpm i --save-dev react-router-dom安装版本 @5.3.0 默认最新的
直接在组件中使用
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom'
//引入路由需要的API
//web端的路由react-router-dom
//移动端 react-router-native
//react-router
//BrowserRouter 是浏览器的历史记录模式
//HashRouter hash 模式网页上的锚标记导航
//Switch 默认匹配第一个子集 写Route时可以倒着写
//Route 路由配置组件
Route组件的各种写法
{/* <Route path='/Index/admin' component={Admin}></Route>
<Route path='/Index/cart' component={Cart}></Route> */}
{/* 下面这个写法直接把路由组件直接添加到route子集 */}
{/* <Route path='/Index/admin'>
<Admin></Admin>
</Route>
<Route path='/Index/cart'>
<Cart></Cart>
</Route> */}
{/* 可以在route组件上添加render 方法 执行返回路由组件 */}
<Route path='/Index/admin' render={(props) => {
console.log(props);
return <Admin></Admin>
}}></Route>
<Route path='/Index/cart' render={(props) => {
return <Cart></Cart>
}}></Route>
Link,NavLink
<Link to='/Index/admin'>admin界面</Link>
{/* activeClassName 激活添加类 */}
<NavLink to='/Index/cart' activeClassName="active">cart界面</NavLink>
Redirect 重定向
<Redirect from='/Index' to='/Index/admin'></Redirect>
from可以不写,to必写。
一级路由代码
{/* 类似vue中的router-view */}
<Router>
{/* 配置路由入口 */}
<Link to="/Index">首页</Link>
<Link to="/my">我的</Link>
{/* exact 属性是bool类型 按照路由的严格模式匹配路由
----等一会测试
*/}
{/* Switch API 默认渲染的是第一个子集
默认的是 /路由
/路由后置
如果添加Switch 不需要属性exact
*/}
{/* <Route exact path='/my' component={My}></Route>
<Route path='/Index' component={Index}></Route> */}
{/* 重定向
必写to from可以省略
*/}
<Switch>
<Route path='/my' component={My}></Route>
<Route exact path='/Index' component={Index}></Route>
<Redirect from='/' to='/Index'></Redirect>
</Switch>
</Router>
React中的路由封装
类似vue的路由模块化
routerView文件
//创建一个类组件
import React, { Component } from 'react';
//引入路由的组件
import { Route, Switch, Redirect } from 'react-router-dom'
class Router_module extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
//获取路由的配置
let { Routes } = this.props;
return (
<Switch>
{/* 配置路由节点 */}
{
// route 路由的配置信息
Routes.map((route, index) => {
return (
<Route exact key={index} path={route.path} render={(nowRoute) => {
return <route.component {...nowRoute} Routes={route.children}></route.component>
//检测当前路由是否存在二级路由
// if (route.children && route.children.length) {
// return (
// // 路由组件属性向下传递
// //组件传值
// <route.component {...nowRoute} Routes={route.children}></route.component>
// )
// }
// else {
// return (
// <route.component {...nowRoute}></route.component>
// )
// }
}}></Route>
)
})
}
<Redirect exact from='/my' to='/my/admin'></Redirect>
<Redirect exact from='/error' to='/'></Redirect>
</Switch>
);
}
}
export default Router_module;
路由的配置文件
// 路由的详细配置
import Index from '../view/Index'
import My from '../view/My'
//二级路由组件
import Admin from '../view/children/Admin'
import Cart from '../view/children/Cart'
let routes = [
{
path: '/',
component: Index
},
{
path: '/my',
component: My,
children: [
{
path: '/my/admin',
component: Admin,
name:'admin'
},
{
path: '/my/cart',
component: Cart,
name:'cart'
}
]
}
]
export default routes;
路由在主模板使用
import { BrowserRouter as Router,Link } from 'react-router-dom'
//引入router模块
import RouterView from '../router/Index'
//引入路由配置
import Routes from '../router/Routes'
function App() {
return (
<div>
<Router>
<Link to='/'>首页</Link>
<Link to='/my'>我的</Link>
<RouterView Routes={Routes}></RouterView>
</Router>
</div>
)
}
export default App;
二级路由配置文件
import React, { Component } from 'react';
//导入封装好的路由模块
import RouterView from '../router/Index'
import { BrowserRouter as Router, Link } from 'react-router-dom'
class My extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
//获取二级路由的相关信息
let { Routes } = this.props;
return (
<div>
我的界面
<Router>
{
Routes.map((route,index)=>{
return (
<Link key={index} to={route.path}>{route.name}</Link>
)
})
}
<hr />
<RouterView Routes={Routes}></RouterView>
</Router>
</div>
);
}
}
export default My;