react 基础

70 阅读8分钟

生命周期

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)

  1. partialState:产生与当前state合并的子集
  2. 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
  1. Provider为后代提供store
  2. 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

  1. 函数内部可以调用其他hook
  2. 只能在function组件和自定义hook中使用
  3. 只能在函数最外层调用,不能在循环,条件,子函数中使用

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>
    );
  }
}