组件
对具有一定独立功能的数据和方法的封装,对外暴露接口,有利于代码功能的复用
1.组件的定义和渲染
函数组件
function assembly(){
return <div>组件</div>
}
const assembly = <Assembly/>;
ReactDOM.render(
assembly,
document.getElementById('root')
);
1.组件类必须继承 React.Component
2.组件类必须有render方法
3.render方法的return后定义组件的内容
4.组件的名称必须大写
5.导出
类式组件
import React,{Component} from 'react';
class App extends Component{
render(){
return ...
}
}
export default App;
或者
import React from 'react'
export default class App extends React.Component{
render(){
return
}
}
在主入口js文件中,导入
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
3.props和state
props
是父组件传递过来的参数
state
是组件自身的状态
this.setState()
参数1:一个函数或一个对象,修改的内容
参数2:回调函数,更新成功后执行
异步方法:react通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能
该方法的赋值属于浅合并(Objecr.assign()
方法),只会进行指定属性的修改,并不是覆盖
原数据
App.js文件
import React,{Component} from 'react';
import FriendList from './FriendList';
export default class App extends Component{
render(){
return (
<div>
//1.父组件传递参数,自定义属性名
<FriendList name="组件传参" />
</div>
)
}
}
FriendList.js文件
import React,{Component} from 'react';
export default class Friend extends Component{
//state的定义
// 1.constructor构造函数中定义
constructor(props){
super(props);
this.state={
date:new Date()
}
}
// 2.作为原型定义
state=()=>{
date:new Date()
}
tick(){
//state只读,无法直接修改
this.setState({
date: new Date()
});
}
render(){
//2.props接收使用this.props
let {name} = this.props
//state接收使用this.state
let {date} = this.state
return (
<div className="friend-list">
{date.toLocaleString()}
//标签内this为undefined,需要使用箭头函数改变this
<button onClick={()=>this.tick()}>点击更新时间</button>
{name}
</div>
)
}
}
4.复合组件
创建多个组件来合成一个组件,根据组件的功能或内容划分
function Name(props) {
return <h1>名字:{props.name}</h1>;
}
function Age(props) {
return <h1>年龄:{props.age}</h1>;
}
function Sex(props) {
return <h1>性别:{props.sex}</h1>;
}
function App() {
return (
<div>
<Name name="马拉卡" />
<Age age=123 />
<Sex sex="男" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
5.组件的生命周期函数
生命周期就是指某个事物从开始到结束的各个阶段,当在 React.js 中指的是组件从创建到销毁的过程
React.js 在这个过程中的不同阶段调用的函数,通过这些函数,我们可以更加精确的对组件进行控制
可分为三个阶段:挂载,更新,卸载
5-1挂载(mounting)
5-1-1 constructor
类的构造函数,组件初始化函数,这个阶段会初始化state,处理事件绑定的this
5-1-2 static getDerivedStateFromProps(props,state)
该方法会在render
方法之前调用,代表数据将要开始挂载
初始挂载及后续更新都会调用
它需要返回一个对象更新state,返回null不更新内容
ps:componentWillMount()
方法16.3版本之前使用,不能与本方法同时使用
5-1-3render()
方法
组件中唯一必须实现的方法
5-1-4componentDidMount()
方法
组件挂载完成后调用
import React,{Component} from 'react';
export default class Friend extends Component{
constructor(props){
super(props);
this.state={date:new Date()}
console.log(1)
}
static getDerivedStateFromProps(){
console.log(2,'将要挂载');
return state;
}
componentDidMount(){
console.log(4,'挂载完成')
}
render(){
console.log(3,'数据渲染')
return (
<div></div>
)
}
}
5-2 数据更新
5-2-0 componentWillReceiveProps(nextProps)
16.3版本之前使用,之后的版本使用加UNSAFE
前缀
父组件引起的子组件更新,16.3版本之前使用
5-2-1static getDerivedStateFromProps(props, state)
render
方法之前调用,初始挂载及后续更新都会调用
该方法不能与componentWillUpdate,componentWillReceiveProps
同时出现,会报错
5-2-2shouldComponentUpdate(nextProps, nextState)
当 props 或 state 发生变化时,该方法会在渲染执行之前被调用,首次渲染不调用
返回值决定是否重新渲染
true
:重新渲染dom,下面的生命周期继续执行
false
:不渲染dom,更新结束
5-2-3 componentWillUpdate()
16.3版本之前使用,之后的版本使用加UNSAFE
前缀
即将更新组件,接收到新的props或state但没有渲染时调用
初始化时,不会调用,该方法中不能调用this.setState()
5-2-4render()
5-2-5getSnapshotBeforeUpdate(prevProps, prevState)
该方法在 render() 之后,但是在输出到 DOM 之前执行
该方法不能与componentWillUpdate,componentWillReceiveProps
同时出现,会报错
5-2-6componentDidUpdate()
更新完成后立即被调用
import React,{Component} from 'react'
export default class Child extends Component{
state = {
age:10
}
UNSAFE_componentWillReceiveProps(nextProps){
console.log(0,"父组件引起的更新")
}
//组件的state或props改变
shouldComponentUpdate(nextProps,nextState){
console.log(1,'state或props改变');
return true;
}
//即将更新组件,接收到新的props或state但没有渲染时调用
UNSAFE_componentWillUpdate(nextProps,nextState){
console.log(2,'组件即将更新')
}
componentDidUpdate(){
console.log(4,'挂载完成')
}
changeAge=()=>{
let {age} = this.state;
this.setState({
age:age+=1
});
}
render(){
let {age} = this.state;
let {name,changeName} = this.props;
console.log(3,"渲染");
return (
<div>
<p>名字:{name}</p>
<p>年龄:{age}</p>
<button onClick={changeName}>升级</button>
<button onClick={
()=>this.changeAge()
}>长一岁</button>
</div>
)
}
}
import React,{Component} from 'react'
export default class Child extends Component{
state = {
age:10
}
static getDerivedStateFromProps(props, state){
console.log(0)
return state
}
//组件的state或props改变
shouldComponentUpdate(nextProps,nextState){
console.log(1,'state或props改变');
return true;
}
getSnapshotBeforeUpdate(){
console.log(3.5,"render方法之后");
return 11;
}
changeAge=()=>{
let {age} = this.state;
this.setState({
age:age+=1
});
}
componentDidUpdate(){
console.log(4,'挂载完成')
}
render(){
let {age} = this.state;
let {name,changeName} = this.props;
console.log(3,"渲染");
return (
<div>
<p>名字:{name}</p>
<p>年龄:{age}</p>
<button onClick={changeName}>升级</button>
<button onClick={
()=>this.changeAge()
}>长一岁</button>
</div>
)
}
}
5-3卸载阶段
5-3-1componentWillUnmount()
该方法会在组件卸载及销毁之前直接调用
app.js
import React,{Component} from 'react';
import Child from './child'
export default class App extends Component{
state = {
name:"baby",
isShow:true
}
changeName=()=>{
this.setState({
name:"bigBaby"
})
}
render(){
let {name,isShow} = this.state
return (
<div>
{isShow?<Child name = {name} changeName = {this.changeName}/>:''}
<button onClick={()=>{
this.setState({
isShow:false
})
}}>卸载</button>
</div>
)
}
}
class.js
import React,{Component} from 'react'
export default class Child extends Component{
state = {
age:10
}
toScroll(){
console.log(111)
}
//渲染前调用
componentWillMount(){
console.log(1)
window.addEventListener("resize",this.toScroll)
}
//在组件从 DOM 中移除之前调用
componentWillUnmount(){
console.log("组件即将卸载")
window.removeEventListener("resize",this.toScroll)
}
changeAge=()=>{
let {age} = this.state;
this.setState({
age:age+=1
});
}
render(){
let {age} = this.state;
let {name,changeName} = this.props;
console.log(3,"渲染");
return (
<div>
<p>名字:{name}</p>
<p>年龄:{age}</p>
<button onClick={changeName}>升级</button>
<button onClick={
()=>this.changeAge()
}>长一岁</button>
</div>
)
}
}
5-4 错误处理
5-4-1static getDerivedStateFromError(error)
后代组件抛出错误后被调用,渲染阶段被调用,
import React,{Component} from 'react'
export default class Child extends Component{
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 错误作为参数参数方法中
//返回一个对象更新state
return { hasError: true };
}
render(){
if (this.state.hasError) {
// 你可以渲染任何自定义的降级 UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
5-4-2componentDidCatch(error,info)
此生命周期在后代组件抛出错误后被调用,提交阶段被调用
参数1:抛出的错误
参数2:带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息
6.组件传参
6-1子组件修改父组件数据
可通过父组件给子组件传一个函数,子组件调用,来达成子组件修改父组件的参数
6-2 跨组件通信context
新建一个js文件
context.js文件
import {createContext} from 'react'
let context = createContext();
let {Consumer,Provider} = context;
export default context
export {Consumer,Provider}
传参
import React,{Component} from 'react';
import FriendList from './FriendList';
import {Provider} from './context'
export default class App extends Component{
render(){
return (
//Provider 是一个标签,包裹本页面的所有标签
//value 要传递的数据
<Provider value={{
info:'别浪'
}}>
<div>
<FriendList/>
</div>
</Provider>
)
}
}
接收参数
import React,{Component} from 'react'
import {Consumer} from './context'
export default class Dl extends Component{
render(){
return (
<div>
<Consumer>
{(value)=>value.info}
</Consumer>
</div>
)
}
}
7.受控组件
受控组件:类似于Vue的双向数据绑定,数据视图互相影响
非受控组件:类似于单项数据流,只可以数据改变视图
8.PureComponent
PureComponent
提供了一个具有浅比较的 shouldComponentUpdate
方法,其他和 Component
完全一致
import React,{Component,PureComponent} from "react";
class App extends PureComponent(Component) {
//Component使用
//使用setState()进行操作时
//可以es6的赋值写法,属性名值相同时,可以简写
this.setState({arr})
//PureComponent使用
//浅比较:更新前后对比,不一样才会更新
//使用setState()操作时,es6的方法无法生效
this.setState({
arr:[...arr]
})
}
export default App;
9.refs
旧版:
绑定:<div ref='自定义'></div>
使用:this.refs.自定义名称
新版:
设置:render方法外自定义名字=createRef()
绑定:<div ref={this.自定义名字}></div>
使用:this.自定义名字.current
import React,{Component,PureComponent, createRef} from "react";
class App extends PureComponent {
//新版设置
box = createRef();
componentDidMount(){
//旧版使用
console.log(this.refs.box)
//新版使用
console.log(this.box.current)
}
render(){
return (
<div
//旧版绑定
ref='box'
//新版绑定
ref = {this.box}
>
</div>
)
}
}
export default App;
10.children
组件标签对之间的内容会被当做一个特殊的属性 props.children
传入组件内容
import React,{Component,PureComponent} from "react";
class Child extends PureComponent{
render(){
let {children}=this.props
return (
<div>
{children}
</div>
)
}
}
class App extends PureComponent {
render(){
let {show,man} = this.state
return (
<div>
<Child>
<h1>好累</h1>
<p>真的好累</p>
{'要死啦'}
{[1,2,3]}
</Child>
</div>
)
}
}
export default App;
11.dangerouslySetInnerHTML
直接设置标签的innerHTML
import React,{Component,PureComponent} from "react";
let message = `<div>憨批</div>`;
class App extends PureComponent {
render(){
return (
//接收一个对象
//接收后端发送 校检过的数据
<div dangerouslySetInnerHTML={{
__html:message
}}>
</div>
)
}
}
export default App;
12.key的问题
在 React
,组件每次更新时,会生成一个 虚拟DOM,和原有的虚拟DOM进行对比
如果是批量生成的一组元素,那React
就会根据key
值去做对比
一个列表中的每一项 key 是唯一的
如果列表中发生顺序等操作变化,key 一定要用数据的id
import React,{Component,PureComponent} from "react";
class App extends PureComponent {
state={
data:[
{id:0,content:'第1条数据'},
{id:1,content:'第2条数据'},
{id:2,content:'第3条数据'},
{id:3,content:'第4条数据'},
{id:4,content:'第5条数据'},
]
}
render(){
let {data} = this.state
return (
<div>
{
data.map((item,index)=>{
return (
<div key={item.id}>
{item.content}
<a onClick={()=>{
data = data.filter(itemData=>itemData.id!==item.id)
this.setState({
data:[...data]
})
}}>删除</a>
</div>)
})
}
</div>
)
}
}
export default App;