生命周期
import React, { Component } from "react";
export default class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {
data:66
};
}
componentWillMount() {
// 组件将要挂载时
// 将要废弃
}
componentDidMount() {
// 组件将要挂载完成
// dom操作,请求数据
}
shouldComponentUpdate(nextProps, nextState) {
// 是否要更新数据时
// 可以不写,写了必须有返回
return true;
}
componentWillUpdate() {
// 组件将要更新
// 将要废弃
}
componentDidUpdate(preProps,preState,snapshot) {
// 组件更新之后
// snapshot:getSonapshotBeforeUpdate传过来的参数
}
componentWillUnmount() {
// 组件将要被卸载
}
componentWillReceiveProps() {
// 父组件中改变了props传值时
// 将要废弃
}
// 新增的生命周期
static getDerivedStateFromProps(props, state) {
// 在render调用方法前调用。在初始加载或者后续更新中被调用。返回对象更i性能state,或者null不更新
return {
data:888
};
}
static getSonapshotBeforeUpdate(preProps,preState){
// 在render之后,componentDidUpdate之前
// 可以在组件发生更换之前获取dom
return null
}
render() {
const {data}=this.state
return <div>{data}</div>;
}
}
setState
- 不要直接修改state
- state的更新可能是异步的
- state的更新可能会被合并
setState(partialState,callback)
- partialState:产生与当前state合并的子集
- callback:state更新之后的回调
import React, { Component } from "react";
export default class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
}
changeValue = (value) => {
//setState 在合成事件中是异步的,会合并操作,批量更新,达到优化性能的目的
//可使用 setTimeout和原生事件进行同步
this.setState(
{
counter: this.state.counter + value,
},
() => {
// 执行之后的回调
console.log("counterfun", this.state.counter);
}
);
console.log("counter", this.state.counter);
};
addBtn = () => {
// this.changeValue(1); //异步
// 同步
setTimeout(() => {
this.changeValue(1);
}, 0);
};
componentDidMount() {
// 同步
document.getElementById("clickDiv").addEventListener("click", this.addBtn);
}
render() {
const { counter } = this.state;
return (
<div id="clickDiv" onClick={this.addBtn}>
{counter}
</div>
);
}
}
router
Route 渲染的优先级 children>component>render
- children:fun
- component:组件
- render:fun
注:component 也可以接受一个函数式,组件需要更新时,会一直卸载,挂载,影响性能, component渲染时候调用react.creactElement , 如果使用匿名函数形式,导致生成的type总是不相同 的,会一直卸载,挂载,影响性能
router/index.js
import ContextPage from "../pages/ContextPage.js";
import TitleContext from "../pages/TitleContext.js";
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/content/:id", //params传参的占位符
element: <ContextPage></ContextPage>,
},
{
path: "/title",
element: <TitleContext></TitleContext>,
},
]);
export default router;
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./store";
import { Provider } from "react-redux";
//路由挂载
import { RouterProvider } from "react-router-dom";
import router from "./router/index";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/* 挂载到根目录 */}
<Provider store={store}>
<RouterProvider router={router}></RouterProvider>
</Provider>
</React.StrictMode>
);
reportWebVitals();
TitleContext.js
import React from "react";
// 跳转方法
import { useNavigate } from "react-router-dom";
export default function FunctionComponent(props) {
const navigate = useNavigate();
return (
<>
<div>title测试</div>
{/* usearchParams传参*/}
<button onClick={() => navigate("/content?id=1111")}>usearchParams传参</button>
{/* params传参 */}
<button onClick={() => navigate("/content/1111")}>params传参</button>
</>
);
}
ContextPage.js
import React from "react";
import { useSearchParams, useParams } from "react-router-dom";
export default function FunctionComponent(props) {
//searchParams 接参数
// const [params] = useSearchParams();
// const id = params.get("id");
// params接参数
const params = useParams();
const id = params.id;
return <div>content测试{id}</div>;
}
redux component
纯粹的状态管理,默认支持同步,实现异步需要使用中间件(redux-thunk、redux-logger) 中间件是一个函数,对store.dispath进行改造
npm i redux --save
store/index.js
import { createStore } from "redux";
function counterRe(state = 0, action) {
switch (action.type) {
case "add":
return state + 1;
case "dec":
return state - 1;
default:
return state;
}
}
const store = createStore(counterRe);
export default store;
使用组件
import React, { Component } from "react";
import store from "./store/";
export default class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
// store 数据更新,触发渲染
store.subscribe(() => {
// class组件强制更新
this.forceUpdate();
});
}
render() {
return (
<>
{/* getState()获取state数值 */}
<div>{store.getState()}</div>;
<button
onClick={() => {
// 触发store中的方法
store.dispatch({
type: "add",
});
}}
>
add
</button>
</>
);
}
}
context
注意:需要把provider的value状态提升至节点的state中,(不要直接在value赋值)避免provider组件重新渲染时候,引起consumer的渲染
TitleProvider.js
import React from "react";
// context的使用
// 创建一个上下问
export const TitleContext = React.createContext({ title: "context默认值" });
// 创建一个提供者
export const TitleProvider = TitleContext.Provider;
// 创建一个使用者
export const TileConsumer = TitleContext.Consumer;
ClassConponent.js
import React, { Component } from "react";
import ContextPage from "./ContextPage";
import { TitleProvider } from "./TitleContext";
export default class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {
title: "context使用",
};
}
render() {
return (
<div>
<TitleProvider value={this.state.title}>
<ContextPage></ContextPage>
</TitleProvider>
</div>
);
}
}
ContextPage.js
//类组件的使用,或者使用consumer
import React, { Component } from "react";
import { TitleContext } from "./TitleContext";
export default class ClassConponent extends Component {
// 把TitleContext 挂载到class上
// 只能订阅一个context,并且是类组件
static contextType = TitleContext;
// this.context 就是要传递过来的值
// 在任何生命周期都可以访问
render() {
return <div>{this.context}</div>;
}
}
// 函数组件的使用
import React from "react";
import { TileConsumer } from "./TitleContext";
export default function FunctionComponent(props) {
return (
<div>
<TileConsumer>
{(cti) => {
return <div>{cti}</div>;
}}
</TileConsumer>
</div>
);
}
react-redux
npm i react-redux --save
- Provider为后代提供store
- connect 为组件提供数据和变更方法
import {useDispatch, useSelector } from "react-redux";
// useDispatch 执行reduce方法
// useSelector 获取state
全局的store index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./store";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/* 挂载到根目录 */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
子组件使用
import React, { Component } from "react";
import { connect } from "react-redux";
export default connect(
// mapDispathTopProps Object/Function 把dispath映射到props 如果不指定 默认是映射的
// Object dispath 本身不会被注入props
// Object
// {
// addFun: () => ({ type: "add" }),
// }
// function
// (dispath) => {
// const res = { addFunc: () => dispath({ type: "add" }) };
// return {
// dispath,
// ...res,
// };
// }
(dispath) => {
const res = { addFunc: () => ({ type: "add" }) };
// bindActionCreators redux提供的函数 简化上述操作的
bindActionCreators(res,dispath)
return {
dispath,
...res,
};
}
(dispath) => {
return {
dispath,
};
},
// mergeProps 上面计算的结果的合并,使用不常
(stateProps,dispathProps,ownProps)=>{
return {msg:"66",...stateProps,...dispathProps,...ownProps}
}
)(
class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {}
render() {
// const { counter, dispatch } = this.props;
const { counter, addFun } = this.props;
return (
<>
<div>{counter}</div>
{/* 无映射时候 dispath的使用 */}
{/* <button
onClick={() =>
dispatch({
type: "add",
})
}
>
add
</button> */}
{/* 映射时候 dispath的使用 */}
<button onClick={addFun}>add</button>
</>
);
}
}
);
PureComponent
Component是React中定义组件的基类,它的shouldComponentUpdate方法默认返回true,也就是说,每次调用setState或forceUpdate方法都会引发组件重新渲染。如果我们希望在一些情况下避免不必要的渲染(例如props或state没有变化),就需要在继承自Component的组件中手动实现shouldComponentUpdate方法来进行比较。
PureComponent则提供了一种基于浅比较的优化机制,它默认实现了shouldComponentUpdate方法,会自动对组件的props和state进行浅比较,如果发现props或state没有发生变化,则阻止组件的重新渲染,提高性能。对于复杂的数据结构,需要注意进行引用类型的比较,避免误判。
Hook
- 函数内部可以调用其他hook
- 只能在function组件和自定义hook中使用
- 只能在函数最外层调用,不能在循环,条件,子函数中使用
useState 状态钩子
纯函数组件没有状态,该钩子为函数组件引入state,并进行数据操作
import React, { useState } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const addOption = () => {
setData(data + 1);
};
return (
<>
<div>{data}</div>
<button onClick={addOption}>add </button>
</>
);
}
useEffect 副作用钩子 (异步执行)
用于在函数组件中进行 接口请求,订阅,监听等操作。模拟类组件中的生命周期(componentDidMount,componentDidUpdate,componentWillUnmount三个函数的组合)
// 接受两个参数,1: 异步操作函数 2:参数是一个数组
import React, { useState, useEffect } from "react";
export function FunctionComponent(props) {
const [data, setData] = useState(0);
// 第二个参数为空 每次渲染都会执行
useEffect(() => {
setData({
data: data + 0,
});
return () => {
// 卸载之后执行
};
});
// 第二个参数为空数组 组件挂载后,运行一下
useEffect(() => {
// 相当于 componentDidMount,componentDidUpdate,componentWillUnmount
return () => {
// 卸载之后执行
};
}, []);
// 第二个参数 有参数 ,监听参数的改变
useEffect(() => {
return () => {
// 卸载之后执行
};
}, [data]);
return <div>{data}</div>;
}
useContext 共享状态钩子
跨越组件层级直接传递变量,实现数据共享
import React, { useState, useContext, createContext } from "react";
const CountContext = createContext(0);
export default function FunctionComponent(props) {
const [count, setCount] = useState(0);
function addCount() {
setCount(count + 1);
}
return (
<>
<div>{count}</div>
<button onClick={addCount}>点击一下</button>
{/* 通过value传值 外面要包裹 xxxContext.Provider*/}{" "}
<CountContext.Provider value={count}>
<Child />
</CountContext.Provider>
</>
);
}
const Child = () => {
// 获取数据
const count = useContext(CountContext);
return <div>子组件{count}</div>;
};
useReducer
存储和更新数据 组件传参
import React, { useEffect, useReducer } from "react";
function setName(state = [], action) {
switch (action.type) {
case "INIT":
return [...action.payload];
case "REPALCE":
return [...action.payload];
default:
return state;
}
}
export default function FunctionComponent(props) {
const [nameList, dispath] = useReducer(setName, []);
useEffect(() => {
dispath({ type: "INIT", payload: ["小红", "小王"] });
}, []);
return (
<div>
<NameList
nameList={nameList}
setName={(parms) => dispath({ type: "REPALCE", payload: parms })}
></NameList>
</div>
);
}
function NameList({ nameList, setName }) {
const deleteItem = (index) => {
const tem = [...nameList];
tem.splice(index, 1);
setName(tem);
};
return (
<div>
{nameList.map((item, index) => {
return (
<div key={item} onClick={(index) => deleteItem(index)}>
{item}
</div>
);
})}
</div>
);
}
useMemo 缓存
import React, { useState, useMemo } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const [counter, setCount] = useState(0);
// 接受两个参数 参1:函数,返回值作为缓存值 。参2:依赖数组
//依赖数组中的值发生变化,重新计算。否则直接取上次计算数据
const result = useMemo(() => {
console.log("useMemo");
return counter;
}, [counter]);
// 如果没有使用useMemo,当data变化的时候,也会执行
// const result = () => {
// console.log("无useMemo");
// return counter;
// };
const addOption = () => {
setData(data + 1);
};
const addCounter = () => {
setCount(counter + 1);
};
return (
<>
<div>{result}</div>
<button onClick={addOption}>data - {data} </button>
<br />
<button onClick={addCounter}>counter - {counter} </button>
</>
);
}
useCallback 渲染性能优化 (缓存函数本身)
import React, { useState, useCallback } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const [counter, setCount] = useState(0);
// 接受两个参数 参1:函数,返回值作为缓存值 。参2:依赖数组
//依赖数组中的值发生变化,重新计算。否则直接取上次计算数据
// 如果没有传入依赖数组,记忆函数每次都会更新
const result = useCallback(() => {
console.log("222");
return counter;
}, [counter]);
const addOption = () => {
setData(data + 1);
};
const addCounter = () => {
setCount(counter + 1);
};
return (
<>
<div>{result()}</div>
<button onClick={addOption}>data - {data} </button>
<br />
<ChildFun addClick={addCounter}></ChildFun>
</>
);
}
function ChildFun(props) {
const { addClick } = props;
console.log("useCallback");
return (
<>
<div>child</div>
<button onClick={addClick}>add</button>
</>
);
}
自定义Hook
import React, { useState, useEffect } from "react";
export default function ClassConponent(props) {
return <div>{useClick().toLocaleTimeString()}</div>;
}
// 自定义hook ,要use开头,
function useClick() {
const [date, setData] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setData(new Date());
}, 10000);
return () => clearInterval(timer);
}, []);
return date;
}
高阶组件
不要在render 中使用高阶组件, diff算法会重新渲染
import React, { Component } from "react";
// Hoc:函数 接收一个组件返回一个组件
const uhoc = (Cmp) => (props) => {
console.log(props); //Child组件
return (
<>
<div>
<Cmp {...props}></Cmp>
</div>
</>
);
};
function Child(props) {
return <div>这是Child组件</div>;
}
const Uhoc = uhoc(Child);
export default class ClassConponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {}
render() {
return (
<div>
<Uhoc data="Child组件"></Uhoc>
</div>
);
}
}