react
jsx就是javascript XML的简写,表示在js代码中写XML(HTML)格式的代码 优势:
- 写JSX就跟写HTML一样,更加直观,友好
- JSX语法更能体现React的声明式特点(描述ui长什么样子)
事件绑定this指向
1.class的实例方法
getData = () => {}
2.箭头函数
<Button onClick={() => this.getData()} />
3.bind
this.getData = this.getData.bind(this)
受控组建 this.state.value ----this.setState() 可以给多个input、select设置name 统一控制 非受控组建
this.texRef = React.createRef();
<input type='text' ref={this.texref} />
<button onChick={()=> console.log('文本框值为:',this.txtRef.current.value)}></button>
props 数据只读
- 父组件--子组件 props
- 子组件--父组件 回调函数
- 兄弟组件--状态提升 在最近的父组件中定义方法及初始值
- 跨组件传递
使用步骤
1.调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件
//创建两个组件 Provider,Consumer
//let {Provider,Consumer}=React.createContext(defaultValue); //defaultValue可以设置共享的默认数据 当Provider不存在的时候 defaultValue生效
const { Provider, Consumer } = React.createContext({ theme: "green" })
function App() {
return (
<>
<Provider value={{ theme: 'pink' }}>
<Content />
</Provider>
</>
)
}
//中间组件
function Content() {
return (
<div>
<Button />
</div>
)
}
//接收组件,如果子组件是Consumer的话,将value作为参数值,传递给新创建的Consumer渲染一个函数组件
function Button() {
return (
<Consumer>
{({ theme }) => (
<button
style={{ backgroundColor: theme }}>
Toggle Theme
</button>
)}
</Consumer>
)
}
props.children
当组件中使用的标签中有文字时可以使用props.children获取
<App>我是子节点呀</App>
props.children ---> 我是子节点呀
children 就是一个特殊的props属性
组件的生命周期 挂载阶段
- constructor 组件创建时 //1.初始化state 2.为事件处理函数绑定this
- render 每次渲染都会触发
- componentDidMount 组件挂载(完成Dom渲染)后//1.发送网络请求 2.可以进行Dom操作
更新阶段
- 组件接收新props 2.setState 3.forceUpdate 执行条件
- render
- componentDidUpdate 注:函数更新时 必对比上一次的数据和当前的数据 if else 因为它拿的是更新后的数据
卸载
- componentWillUnmount //清理定时器
render props 模式使用步骤
方法1:
function App(props) {
return (
<Child
getData={data => { 3.组件使用工具组件,获取暴露的方法,拿到数据,渲染Ui
return(
<p>{data.name}</p>
)
}}
/>
)
}
class Child extends React.Component {
1.方法组建提供复用的状态逻辑代码(1.状态 2.操作状态的方法
state = {
name: 'bob'
}
render() {
2.方法组件要复用的状态数据,通过this.props.getData方法的参数,暴露到组件的外部
return this.props.getData(this.state)
}
}
方法2: render porps方式渲染
function App(props) {
return (
<Child>
{data =>{
return (
<div>{data.name}</div>
)
}}
</Child>
)
}
class Child extends React.Component {
state = {
name: 'bob'
}
render() {
return this.props.children(this.state)
}
}
高阶组件 目的:实现状态逻辑复用 displayName 设置多个高阶组件的名字
function WithMouse(WrappedComponent) {
// class Mouse extends React.Component {
// state = {
// x: 0,
// y: 0
// }
// handMouseMove = e => {
// this.setState({
// x: e.clientX,
// y: e.clientY
// })
// }
// componentDidMount() {
// window.addEventListener('mousemove', this.handMouseMove)
// }
// componentWillUnmount() {
// window.removeEventListener('mousemove', this.handMouseMove)
// }
// render() {
// return <WrappedComponent {...this.state} {...this.props}/>
// }
// }
// return Mouse;
const Mouse = () => {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const handMouseMove = e => {
setX(e.clientX)
setY(e.clientY)
}
useEffect(() => {
window.addEventListener('mousemove', handMouseMove)
return () => {
window.removeEventListener('mousemove', handMouseMove)
}
}, [])
const state = {
x, y
}
return <WrappedComponent {...state} />
}
return Mouse;
}
const Position = props => {
return (
<p>我的名字是:{props.name}鼠标当前的位置 x:{props.x} y:{props.y}</p>
)
}
const MousePositon = WithMouse(Position);
function App() {
return (
<div>
<h1>高阶组建</h1>
<MousePositon name={'bob'}/>
</div>
)
}
this.setState 调用多次只触发一次render
state = 1;
Chick =()=>{
this.setState({count:this.state.count + 1})
this.setState({count:this.state.count + 1})
}//count为2
Chick = () => {
this.setState((state, props) => { 回掉函数写法
return {
count: state.count + 1
}
})
this.setState((state, props) => { //state可拿到最新值
return {
count: state.count + 1
}
})
}//count为3
//在状态更新后(页面完成重新渲染)后立即执行某个操作
this.setState(
(state, props)=>{},
() => {console.log('这个回调函数会在状态更新后立即执行,可以操作Dom')}
)
组件更新机制
this.setState作用
1.修改state 2.更新组件(Ui)
父组件更新,它下面的所有子组件都会向下流更新
组建性能优化
- state只存储和页面渲染相关的数据,计时器id这种可以直接放在this中
- 因为父组件更新会影响子组件所以为了避免不必要的更新
- 组件使用nexProps 最新的nexProps.count 当前的 this.props.count
- 自身使用nextState 最新的nextState.count 当前的 this.state.count
先走 shouldComponentUpdate(nexProps, nextState){
条件判断
true表示渲染
false表示不重新渲染
}
再根据return true或者false 走render
3.pureComponent 因为在pureComponent 中引用类型比较是看地址 ,所以想要改变一个引用类型的值,应该创建数据,不要直接修改数据,否则pureComponent会认为没有改变,无法更新
[]
不要使用push / unshift 等直接修改当前数组的方法
使用concat或slice等这些返回新数组的方法
this.setState({
list:[...this.state.list,{新数据}]
})
obj
this.setState({
obj:[...this.state.obj,{新数据}]
})
虚拟Dom和diff算法 1.虚拟Dom:本质上就是一个js对象,用来描述屏幕上看到的内容Ui(html结构
执行过程
- 初次渲染时,React根据初始state,创建虚拟dom树
- 根据虚拟Dom生成真正的Dom,渲染到页面中
- 当数据改变(this.setState(),重新根据新的数据,创建新的虚拟dom树
- 与上一次的虚拟dom对象,使用Diff算法对比(找不同,得到需要更新的内容
- 最终,react只将变化的内容更新(patch)到DOM中,重新渲染页面
render时候会重新生成虚拟DOM对象 和初始的虚拟Dom对比 虚拟Dom的作用不仅是提升了react的性能,更多的是可以让js在更多平台运行
useEffect()
componentDidMount() + componentDidUpdate()
useCallback() + memo
import React, { useCallback, useState, memo } from 'react';
import Expensive from './components/Expensive';
import Child from './components/Child';
const MemoExpensive = memo(Expensive); //组件使用memo 方式1
const AddAssetsDetail = () => {
const [data1, setData1] = useState(0);
const [data2, setData2] = useState(0);
const onClick1 = () => {
setData1(num => num + 1);
};
const onClick2 = useCallback(() => {
setData2(num => num + 1);
}, []);
return (
<div>
<Child onClick={onClick1} data={data1} />
<MemoExpensive onClick={onClick2} data={data2} />
</div>
)
}
export default AddAssetsDetail;
import React, { memo } from 'react';
import { Button } from 'antd';
const Expensive = memo((props) => { //组件使用 memo 方式2
const { data, onClick } = props;
console.log('Expensive执行了。。。。。');
return (
<div>
Expensive.....{data}
<Button onClick={onClick}>点击增加</Button>
</div>
)
})
export default Expensive;
import React from 'react';
import { Button } from 'antd';
function Child(props) {
const { data, onClick } = props;
console.log('Child执行了。。。。。');
return (
<div>
Child.....{data}
<Button onClick={onClick}>点击增加</Button>
</div>
)
}
export default Child;
memo是 React v16.6.0 新增的方法,与 PureComponent 类似,前者负责 Function Component 的优化,后者负责 Class Component。它们都会对传入组件的新旧数据进行浅比较,如果相同则不会触发渲染。
所以useCallback保证了onClick2不发生变化,此时点击Child组件不会触发Expensive组件的刷新,只有点击Expensive组件才会触发。在实现减少不必要渲染的优化过程中,useCallback和memo是一对利器
useMomo()
import React, { memo, useMemo } from 'react';
import { Button } from 'antd';
const Child = memo((props) => {
const { a, b } = props;
const sum = () => {
//a,b值改变触发了大量的运算
};
const result = useMemo(() => { sum() }, [a, b]);
return (
<div>
<Button onClick={result}>点击增加</Button>
</div>
)
})
export default Child;
useContext()
父组件
import React, { useState } from "react";
import ReactDom from 'react-dom';
import Child from './Child';
export const NumberContext = React.createContext(); (第1步
const App = () => {
const [number, setNumber] = useState(2022);
return (
<NumberContext.Provider value={number}> (第2步
<Child />
</NumberContext.Provider>
)
}
ReactDom.render(<App />, document.getElementById('app'));
中间组件
import React from "react";
import SmallChild from './SmallChild';
const Child = () => {
return (
<div>
<SmallChild></SmallChild>
<div>这个是中间组建自己的逻辑</div>
</div>
)
}
export default Child;
孙子组件
import React, { useContext } from "react";
import { NumberContext } from './index'; (第3步
const SmallChild = () => {
let number = useContext(NumberContext); (第4步
return (
<h2>这个是父组建传过来需要展示的数据,今年是{number}年</h2>
)
}
export default SmallChild;
useReducer()
import React, { useReducer } from "react";
import ReactDom from 'react-dom';
import '$css/index.less';
const initialState = 999
const reducer = (state, action) => {
switch (action) {
case 'add':
return state + 2
case 'sub':
return state - 2
case 'reset':
return initialState
default:
return state
}
}
const App = () => {
const [number, dispatch] = useReducer(reducer, initialState);//默认值
return (
<div>
<h2>点击{number}就进行加减</h2>
<button onClick={() => { dispatch('add') }}>add点我吧!</button>
<button onClick={() => { dispatch('sub') }}>sub点我吧!</button>
</div>
)
}
ReactDom.render(<App />, document.getElementById('app'));