react 常见技巧

77 阅读3分钟

render prop

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>

        {/*
          使用 `render`prop 动态决定要渲染的内容,
          而不是给出一个 <Mouse> 渲染结果的静态表示
        */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

`

HOC的使用

export default function Index() {
  return (
    <div>
      <HOC></HOC>
      <HOC1
        render={(render: any) => (
          <div>
            <div>{render.type}</div>
          </div>
        )}
      ></HOC1>
    </div>
  );
}

HOC的定义

export function HOC(WrappedComponent) {
  const newProps = { type: "HOC" };
  return (props) => <WrappedComponent {...props} {...newProps} />;
}
--------------------------------------
import React from "react";
import HOC, { HOC1 } from "src/hooks/Hoc.jsx";

function testHoc() {
  return <div>useHoc</div>;
}
export default HOC(testHoc);
--------------------------------------------
export function HOC1(prop) {
  const newProps = { type: "HOC" };
  return <div>{prop.render(newProps)}</div>;
  //  return (props) => <WrappedComponent {...props} {...newProps} />;
}

react的事件机制

阻止合成事件向原生事件的冒泡采用e.nativeEvent.stopImmediatePropagation

useState闭包问题

import React, { useState } from "react";

const Demo1 = () => {
  //   这是因为在函数组件中,每次点击add count按钮都会使得count自增一次=>导致Demo1函数重新执行一遍,形成一个新的闭包。每个闭包中的count值是相互独立的。点击alert按钮的这一瞬间形成的闭包中count的值为5,3s之后弹出来的这个值自然也为5。后面再次自增,形成的是新的闭包。互不干扰。
  // 而在Class组件中由于都是在this的上下文中改变count的值,而不会形成隔离的闭包,因此,虽然点击alert按钮那一时刻count的值为5,但是随着后面点击add count按钮,count值不断自增。修改的都是this上面的值,3s后弹出的也就是count的最新值了。
  let [count, setCount] = useState(0);
  const addCount = () => {
    setCount(count + 1);
  };
  const alertCount = () => {
    setTimeout(() => {
      alert(count);
    }, 3000);
  };

  return (
    <div>
      <button onClick={addCount}>add Count</button>
      <button onClick={alertCount}>alertCount</button>
      <div>count: {count}</div>
    </div>
  );
};

export default Demo1;

父组件调用子组件方法

//子组件
import React, { forwardRef, useImperativeHandle } from "react";
import { Modal } from "./components/children";
const ChildComp = forwardRef(({ value, onChange }, ref) => {
  function hah() {
    console.log(111);
  }
  useImperativeHandle(ref, () => ({
    hah,
  }));
  return (
    <div style={{ marginTop: 100, width: 300 }} ref={ref} onClick={hah}>
      <div style={{ width: "100%", background: "red" }}>112</div>
      <Modal>
        <div style={{ width: "100%", height: "100%", background: "red" }}>
          弹窗内容
        </div>
      </Modal>
    </div>
  );
});
export default ChildComp;
// 父组件
import React, { Component, useRef } from "react";
import ChildComp from "./ChildComp";
export default function App() {
  const childRed = useRef(null);
  function haha() {
    childRed.current.hah();
  }
  return (
    <div>
      App
      <ChildComp ref={childRed} />
      <div onClick={haha}>调用子组件hah方法</div>
    </div>
  );
}
****vue3也是这样再父组件调用子组件方法#####

类组件

// es6 class
// 实例属性的以前的写法
constructor(){
  this._count = 0  
}
// 新的写法
_count = 0
// 类组件的定义
import React, { Component } from "react";
import Context from "./views/Context";
export default class App extends Component {
  //name = "1";
  constructor(props) {
    super(props);
    this.state = {
      list: ["中国", "美国"],
    };
  }
  render() {
    this.result = this.state.list.map((v, index) => <li key={index}>{v}</li>);
    return (
      <div>
        {this.result}
        <Context />
      </div>
    );
  }
}

类组件事件

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,这个绑定是必不可少的    this.handleClick = this.handleClick.bind(this);  }

  handleClick() {    this.setState(prevState => ({      isToggleOn: !prevState.isToggleOn    }));  }
  render() {
    return (
      <button onClick={this.handleClick}>        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

useContext使用流程

//1.新建Context.js
import { createContext } from "react";
export const ImageSizeContext = createContext(500);

//使用
export default function App() {
  const [isLarge, setIsLarge] = useState(false);
  const imageSize = isLarge ? 150 : 100;
  return (
    <ImageSizeContext.Provider value={imageSize}>
      <label>
        <input
          type="checkbox"
          checked={isLarge}
          onChange={(e) => {
            setIsLarge(e.target.checked);
          }}
        />
        Use large images
      </label>
      <hr />
      <List />
    </ImageSizeContext.Provider>
  );
}

function List() {
  const listItems = places.map((place) => (
    <li key={place.id}>
      <Place place={place} />
    </li>
  ));
  return <ul>{listItems}</ul>;
}

function Place({ place }) {
  return (
    <>
      <PlaceImage place={place} />
      <p>
        <b>{place.name}</b>
        {": " + place.description}
      </p>
    </>
  );
}

function PlaceImage({ place }) {
  const imageSize = useContext(ImageSizeContext);
  return (
    <img
      src={getImageUrl(place)}
      alt={place.name}
      width={imageSize}
      height={imageSize}
    />
  );
}

keep-alive与权限路由实现流程(Router v5版本)

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Redirect,
} from "react-router-dom";
import Child1 from "./components/Child1.jsx";
import Child2 from "./components/Child2.jsx";
import { saveToken, deleteToToken } from "./utils/storage.js";
import PriviteRoute from "./components/priviteRoute.jsx";
import KeepAlive from "./components/keepAlive.jsx";
function App() {
  //保存token
  const saveToToken = () => {
    saveToken();
  };
  return (
    <div>
      <div>
        <button onClick={saveToToken}>保存token</button>
        <button onClick={deleteToToken}>清除token</button>
      </div>
      <div>
        {/* 定义路由出口 */}
        <Router>
          <nav>
            <Link to="/child1">Link: go to child1</Link>
            <Link to="/child2">Link: go to child2</Link>
          </nav>
          <Switch>
            {/* 根组件默认展示,要添加 exact 属性,防止二次渲染 */}
            {/* <Route path="/" component={Child1} exact /> */}
            {/* 路由重定向 */}
            {/* <Redirect from="/" to="/child1" exact /> */}
            <Redirect from="/" to="/child1" exact />;
            <PriviteRoute path="/child1">
              <KeepAlive path="/child1">
                <Child1></Child1>
              </KeepAlive>
            </PriviteRoute>
            <Route path="/child2/:id" component={Child2} />
          </Switch>
        </Router>
      </div>
    </div>
  );
}
export default App;

// 定义(keep-alive)
import React from "react";
import { Route } from "react-router-dom";
export default function KeepAlive({ children, ...rest }) {
  return (
    <Route
      {...rest}
      children={(props) => {
        // console.log(props.match);
        return (
          <>
            <div style={{ display: props.match ? "block" : "none" }}>
              {children}
            </div>
          </>
        );
      }}
    ></Route>
  );
}
// 定义权限路由
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { getToken } from "../utils/storage.js";
export default function PriviteRoute({ children, ...rest }) {
  //   console.log(!!getToken());
  return (
    <Route
      {...rest}
      render={() => {
        if (!!getToken()) {
          return children;
        } else {
          return (
            <Redirect
              to={{
                pathname: "/child2",
                // state: {
                //   from: location.pathname,
                // },
              }}
            ></Redirect>
          );
        }
      }}
    ></Route>
  );
}