1. setState的使用
import React, { Component } from "react";
import styled, { ThemeProvider } from "styled-components";
function Child(props) {
return <p>{props.name}</p>;
}
const TTest = styled.p`
font-size: ${(props) => props.theme.fontSize};
`;
class StateCombine extends Component {
state = {
name: "666",
age: 80,
count: 1,
};
changeName() {
this.setState({
name: "888",
});
// state的数据合并的原理就是下面的浅复制
const result = Object.assign({}, { name: "666", age: 80 }, { name: "888" });
console.log(result);
}
changeCount() {
this.setState({
count: this.state.count + 1,
});
this.setState({
count: this.state.count + 1,
});
this.setState({
count: this.state.count + 1,
});
// 原理也是 Object.assign, 后面会覆盖前面的
const result = Object.assign({}, { count: 1 }, { count: 2 });
console.log(result);
}
changeCount2() {
// setState的第一个参数是函数的时候,会执行函数(和对象不一样),用call方法
this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
}
render() {
const { name, age, count } = this.state;
return (
<ThemeProvider theme={{ fontSize: "30px" }}>
<h3>state的数据合并</h3>
<hr />
<TTest>{name}</TTest>
<p>{age}</p>
<button onClick={() => this.changeName()}>数据的合并改变</button>
<h3>setState的合并</h3>
<hr />
<p>{count}</p>
<button onClick={() => this.changeCount()}>setState的合并改变</button>
<h3>合并时进行累加</h3>
<hr />
<button onClick={() => this.changeCount2()}>setState的合并累加</button>
</ThemeProvider>
);
}
}
export class TState extends Component {
state = {
name: "张三",
};
componentDidUpdate() {
console.log(this.state.name, "componentDidUpdate");
}
/*
为什么 setState 是异步的
1. 可以显著提升性能 每次setState都更新,意味着render会频繁调用,界面重新渲染,效率很低,
所以采用的是批量更新
2. 同步更新了state后,render函数还没有执行,state和props就会出现不一致的情况
*/
changeName = () => {
this.setState(
{
name: "苏里斯",
},
() => {
// 如果要获取最新的name,可以在这里获取,或者在componentDidUpdate获取,这里是在componentDidUpdate后面执行的
console.log(this.state.name, "callback");
}
);
// 这边没有改变,所以setState可以说是异步的(同步有几种特殊情况)
console.log(this.state.name); // 张三
};
// setState同步的情况1
changeNameSync = () => {
setTimeout(() => {
this.setState({
name: "同步演示",
});
console.log(this.state.name, "同步"); // 这个会在componentDidUpdate后面执行
}, 0);
};
componentDidMount() {
// setState同步的情况2 dom操作里面
document
.getElementById("dom")
.addEventListener("click", this.domHandler.bind(this));
}
domHandler() {
this.setState({
name: "同步演示",
});
console.log(this.state.name, "同步dom"); // 这个会在componentDidUpdate后面执行
}
render() {
const { name } = this.state;
return (
<div>
<h3>setState为啥是异步的,即时获取state的方式</h3>
<hr />
<p>{name}</p>
<Child name={name}></Child>
<p>
<button onClick={this.changeName}>改变名字</button>
</p>
<h3>setState同步的情况</h3>
<hr />
<p>
<button onClick={this.changeNameSync}>改变名字同步</button>
</p>
<p>
<button id="dom">改变名字 DOM方式</button>
</p>
<StateCombine></StateCombine>
</div>
);
}
}
export default TState;
2.Ref的使用
import React, { Component, createRef, forwardRef } from "react";
// function FChild() {
// return <p>这是个函数组件</p>;
// }
// 传递ref
const Fr = forwardRef((props, ref) => {
return <p ref={ref}>这是个函数组件</p>;
});
class CChild extends Component {
render() {
return <p>这是个class组件</p>;
}
say() {
console.log("ref调用方法");
}
}
// ref直接用字符串的方式不推荐,推荐使用createRef,对象的方式
export class TRef extends Component {
cref = createRef();
dref = createRef();
fref1 = createRef();
fref2 = createRef();
render() {
return (
<div>
<hr />
<h3>Ref使用</h3>
<p ref={this.dref}>dom元素ref测试</p>
<p ref={(dom) => (this.fdom = dom)}>函数的模式获取dom</p>
<CChild ref={this.cref} />
{/* <FChild ref={this.fref1} /> */}
<Fr ref={this.fref2} />
<button onClick={() => this.printRef()}>打印ref</button>
</div>
);
}
printRef() {
console.log(this.cref.current, this.dref.current);
this.cref.current.say();
console.log(this.fdom);
console.log(this.fref1.current, this.fref2.current); // fref1是null,而且会警告,函数组件不能直接获取ref
}
}
export default TRef;
3. 性能优化
import React, { Component, Fragment, PureComponent, memo } from "react";
// react更新流程
/*
** prop/state改变 => render函数重新执行 => 产生新DOM树 => 新旧DOM树diff算法,计算差异,进行更新 => 更新到真实DOM
** React diff比较
1.同层节点之间比较,不会跨节点比较
2.不同类型的节点,产生不同的树结构(类型不同,直接销毁包括子节点,不会再子节点比较,类型相同,保留DOM节点,然后比较改变的属性)
3.可以通过key指定哪些节点在不同的渲染下保持稳定 (子节点递归)
[比如数组最后插入,前面的没有变化,那前面的就不会销毁,最后一个添加即可]
[如果是在第一个插入,那就会发现第一个发生差异,接着第二个,第三个...,都会发生差异,
所以这种用key的话就能保持稳定,只需要更换位置即可,提高性能]
*/
// render调用
function Header() {
console.log("Header render");
return <h4>我是个头部</h4>;
}
// 函数式组件没有PureComponent,所以用memo
const MemoFooter = memo(() => {
console.log("Footer render");
return <h4>我是个尾部</h4>;
});
function Banner() {
console.log("Banner render");
return <h4>我是个Banner</h4>;
}
function List() {
console.log("List render");
return <h4>我是个List</h4>;
}
class Main extends PureComponent {
render() {
console.log("Main render");
/* 用了PureComponent,父组件的count的变化不会导致Main的更新,Main没有render,那里面的子组件自然不会更新 */
return (
<Fragment>
<Banner></Banner>
<List />
</Fragment>
);
}
}
export class TShouldUpdate extends Component {
state = {
count: 1,
};
changeCount = () => {
this.setState({
count: this.state.count + 1,
});
};
render() {
console.log("TShouldUpdate render");
const { count } = this.state;
return (
<div>
<h4>{count}</h4>
{/* 父组件的render调用,会带动子组件的更新,触发子组件的render */}
{/* 然而这个count跟下面的子组件没有关系,不应该触发子组件的更新 */}
{/* 此时就应该进行shouldComponentUpdate的浅比较,优化性能,使用PureComponent(state或props发生改变) */}
<button onClick={this.changeCount}>增加计数</button>
<Header></Header>
<Main></Main>
<MemoFooter></MemoFooter>
</div>
);
}
}
export default TShouldUpdate;