state 和 setState
不使用setState修改state虽然会修改数据,但是并不会重新渲染视图
setState(updater, [callback])
-
updater: 更新数据
function/object
-
obj中存储的是要修改的状态,setState本身会帮助我门合并状态
this.setState({ name: "修改的名字" })
-
使用function时,setState会执行该函数,返回值必须是一个对象,对象中存储要修改的状态
this.setState(function() { return { name: "修改name" } })
-
-
callback: 更新成功后的回调
FUNCTION
-
异步:react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能
-
浅合并 Objecr.assign()
-
调用 setState 之后,会触发生命周期,调用render() 方法重新渲染组件
setState 的异步优化
在使用setState时,要注意是异步方法,所以在setState之后获取state还是修改之前的内容
<button onClick={() => {
this.setState({
age: ++age
});
console.log(this.state)
}}>+1</button>
优化操作:
多次setState的时候并不是直接调用render,而是把操作存入队列,在没有新的修改之后再执行,这样极大优化了频繁操作时的性能
组件间通信
在 React.js 中,数据是从上自下流动(传递)的,也就是一个父组件可以把它的 state / props 通过 props 传递给它的子组件
但是子组件不能修改 props - React.js 是单向数据流,如果子组件需要修改父组件状态(数据),是通过回调函数方式来完成的。
父级向子级通信
把数据添加子组件的属性中,然后子组件中从props属性中,获取父级传递过来的数据
import React, { Component } from 'react';
import Children from "./Children"
class App extends Component {
state = {
name: "lc",
};
render() {
let { name } = this.state;
return <div>
<Children name={name}/>
</div>;
}
}
export default App;
import React, { Component, Fragment } from 'react';
class Children extends Component {
state = {
age: 6
};
render() {
let { age } = this.state;
let { name } = this.props;
return <Fragment>
<p>{name}</p>
// ...
</Fragment>;
}
}
export default Children;
子级向父级通信
在父级中定义相关的数据操作方法(或其他回调), 把该方法传递给子级,在子级中调用该方法父级传递消息
父级定义一个方法并传递给子组件
editName = (newName) => {
this.setState({
name: newName
});
};
render() {
let { name } = this.state;
return <Children name={name} editName={this.editName} />;
}
子级接收方法并使用
import React, { Component, Fragment } from 'react';
class Children extends Component {
state = {
age: 6
};
render() {
let { age } = this.state;
let { name, editName } = this.props;
console.log(this.props);
return <Fragment>
<p>{name}</p>
<button onClick={() => {
editName("新的名字");
}}>修改名字</button>
</Fragment>;
}
}
export default Children;
跨组件通信 context
注意在使用不熟练时,最好不要再项目中使用 context,context一般给第三方库使用
方法引入
React.createContext(defaultValue);
两个组件
const { Consumer, Provider } = createContext(defaultValue)
- Provider 负责向子孙级组件传递信息
- value 是要传递的数据
- Context 负责接收父级组件的信息
创建context文件
import { createContext } from 'react';
const context = createContext();
const { Provider, Consumer } = context;
export default context;
export { Provider, Consumer };
父级使用Provider组件
Provider组件包裹子组件使用value属性传递数据
import React, { Component } from 'react';
import Children from "./Children";
import { Provider } from "./Context";
class App extends Component {
state = {
name: "lc",
};
editName = (newName) => {
this.setState({
name: newName
});
};
render() {
let { name } = this.state;
return <Provider value={{
name,
editName: this.editName
}}>
<Children />;
</Provider>;
}
}
export default App;
子组件通过Consumer组件接收数据
import React, { Component, Fragment } from 'react';
import { Consumer } from "./Context";
class Children extends Component {
render() {
return <Consumer>
{
(context) => {
console.log(context);
let { name, editName } = context;
return <Fragment>
<p>{name}</p>
<button onClick={() => {
editName("新的名字");
}}>修改名字</button>
</Fragment>;
}
}
</Consumer>;
}
}
export default Children;
另一种写法
import React, { Component, Fragment } from 'react';
import context from "./context";
class Children extends Component {
render() {
console.log(this);
let { name, editName } = this.context;
return <Fragment>
<p>{name}</p>
<button onClick={() => {
editName("新的名字");
}}>修改名字</button>
</Fragment>;
}
}
Children.contextType = context;
export default Children;
或者使用es6 的静态属性
import React, { Component, Fragment } from 'react';
import context from "./context";
class Children extends Component {
static contextType = context;
render() {
console.log(this);
let { name, editName } = this.context;
return <Fragment>
<p>{name}</p>
<button onClick={() => {
editName("新的名字");
}}>修改名字</button>
</Fragment>;
}
}
export default Children;
组件的生命周期
所谓的生命周期就是指某个事物从开始到结束的各个阶段,当然在 React.js 中指的是组件从创建到销毁的过程
React.js 在这个过程中的不同阶段调用的函数,通过这些函数,我们可以更加精确的对组件进行控制,前面我们一直在使用的 render 函数其实就是组件生命周期渲染阶段执行的函数
生命周期 演变
挂载阶段 (组件初始化)
组件创建–>把组件创建的虚拟DOM,生成真实DOM,添加到我们的DOM树中
- constructor 初始化组件
constructor(props) {
super(props);
console.log("初始化组件");
};
- static getDerivedStateFromProps(props) 将props和state进行关联
static getDerivedStateFromProps(props) {
console.log("将props和state进行关联", props);
return {
name: 123 // 添加内容
};
}
该方法返回值必须是个对象,对象属性会加到组件实例的state中
是个静态方法
-
render 创建虚拟DOM
-
componentDidMount – 处理副作用(请求),初始化组件完成,添加到DOM树中
componentDidMount() {
console.log("初始化组件完成,添加到DOM树中")
}
请求数据都会在这里获取
更新阶段 – 组件重新渲染
- static getDerivedStateFromProps(props, state)
static getDerivedStateFromProps(props) {
console.log("1.组件开始更新,获取新的props,并将props绑定在state中", props);
return {
name: 123 // 添加内容
};
};
-
shouldComponentUpdate() – 判断是否更新
shouldComponentUpdate(nextProps, nextState) { console.log("2.判断组件是否需要更新,此时state和props并未改变", this.state, nextState); // 该方法必须有返回值,返回值是个布尔值,true则继续向下执行生命周期,完成组件更新,false则停止组件更新,后续生命周期则不被调用 return true; }
- 该方法必须有返回值,返回值是个布尔值,true则继续向下执行生命周期,完成组件更新,
- false则停止组件更新,后续生命周期则不被调用
-
render()
render() {
console.log("3.重新生成虚拟DOM,准备更新组件,和老的虚拟DOM做出对比,选择更新");
- getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate() {
console.log("4.即将更新新的虚拟DOM,更新真实DOM,在这里可以获取更新前的DOM快照");
let age = document.querySelector("#age");
return age; // 此返回值会被传递给下一周期函数
}
返回值会被传递给下一周期函数
- componentDidUpdate() – 处理副作用(异步请求)
componentDidUpdate(prevProps, prevState, prevDom) {
console.log("5.组件更新完成,state,props以及视图皆已更新,可通过参数获取修改前的props和state");
console.log("第三个形参是上一周期函数传递过来的", prevDom);
console.log(this.state);
}
卸载阶段
- componentWillUnmount – 删除添加在全局的一些信息或操作
组件在挂在阶段可能会有全局方法挂载
componentDidMount() {
window.onresize = function() {
let age = document.querySelector("#age");
console.log(age.innerHTML)
}
}
组件卸载后此方法就会出现问题,所以一般是在卸载前先把这些方法卸载
componentWillUnmount() {
console.log("组件即将被卸载");
window.onresize = null;
}
受控与非受控组件
受控组件
当想要获取表单的一些内部状态时(表单中的checked,value属性等),就可以将表单的内部状态和组件的状态进行绑定,这样就形成受控组件
- 受控组件: 让 表单控件 的内部状态 和我们 state 保持一致
class App extends Component {
state = {
name: "",
checked: false,
};
render() {
let { name, checked } = this.state;
return <Fragment>
<div>
<input type="text"
placeholder="请输入昵称"
value={name}
onChange={({ target }) => {
this.setState({
name: target.value
});
}} />
<span>{name}</span>
</div>
<div>
是否学习?:
<input
type="checkbox"
checked={checked}
onChange={({ target }) => {
this.setState({
checked: target.checked
});
}} />
</div>
<button onClick={() => {
console.log(this.state);
}}>提交数据</button>
</Fragment>;
}
}
非受控组件:
我们不需要同步 value 值(defaultValue
,defaultChecked
)
只是给表单控件一个初始值
<div>
<input type="text"
placeholder="请输入昵称"
defaultValue={name}
/>
<span>{name}</span>
</div>
<div>
是否学习?:
<input
type="checkbox"
defaultChecked={checked}
/>
</div>