react-组件进阶

1.目标

能够实用props接收数据

能够实现父子组件之间的通讯

能够实现兄弟组件之间的通讯

能够给组件添加props校验

能够说出生命周期常用的钩子函数

能够知道高阶组件的作用

2.目录

组件通讯介绍

组件的props

组件通讯的三种方式

Context

props深入

组件的生命周期

Render-props和高阶组件

3.react tool

安装 React Devloper Tools 插件

4.组件通讯介绍

组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。在组件化过程中,我们将一个完整

的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要

共享数据。为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通

讯。

5.组件的props

A. 组件是封闭的,要接收外部数据应该通过props来实现

B. props的作用:接收传递给组件的数据

C. 传递数据:给组件标签添加属性

D. 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据

javascript 复制代码
function Hello(props) {
  return <div>接收到的值:{props.name}</div>;
}

class Hello1 extends React.Component {
  constructor() {
    super();
  }
  render() {
    return <div>接收到的值:{this.props.name}</div>;
  }
}

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Hello name="张三"></Hello>
    <Hello1 name="李四"></Hello1>
  </React.StrictMode>
);

5.1 特点

A. 可以给组件传递任意类型的数据

B. props是只读的对象,只能读取属性的值,无法修改对象

C. 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法再构造函数中获取

到props!

js 复制代码
class Hello2 extends React.Component {
	constructor(props) {
		//推荐将props传递给父类构造函数 ???
		super(props)
	}
render() {
	return (
		<div>
			接收到的数据:{this.props.age}
		</div>
		)
	}
}
ReactDOM.render(<Hello2 name='rose' age={22}></Hello2>,document.getElementById('root'));

5.2 其他类型数据

js 复制代码
class Hello2 extends React.Component {
	constructor() {
		//推荐将props传递给父类构造函数
		super()
	}
render() {
	return (
		<div>
			接收到的数据:{this.props.age} {this.props.tag}
		</div>
	)
}
ReactDOM.render(<Hello2 name='rose' age={22} colors={['red', 'yellow', 'blue']}
fn={() => { console.log('这是一个函数') }} tag={<p>这是一个P标签</p>}></Hello2>,
document.getElementById('root'));

6.组件通讯的三种方式

组件之间的通讯分为3种:

A. 父组件->子组件

B. 子组件->父组件

C. 兄弟组件

6.1 父组件传递数据给子组件

A. 父组件提供要传递的state数据

B. 给子组件标签添加属性,值为state中的数据

C. 子组件中通过props接收父组件中传递的数据

css 复制代码
.parent {
  background-color: skyblue;
  height: 200px;
}

.child {
  background-color: pink;
  height: 100px;
}
js 复制代码
// 父组件给子组件传值
class Parent61 extends React.Component {
  state = { lastName: "练" };
  render() {
    return (
      <div className="parent">
        父组件:{this.state.lastName + "凝"}
        <Child61 lastName="道济"></Child61>
      </div>
    );
  }
}

const Child61 = (props) => {
  return (
    <div className="child">子组件接受到父组件传的值:{props.lastName}</div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <Parent61></Parent61>
);

6.2 子组件传递数据给父组件

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。

A. 父组件提供一个回调函数(用于接收数据)

B. 将该函数作为属性的值,传递给子组件

C. 子组件通过props调用回调函数

D. 将子组件的数据作为参数传递给回调函数

注意:回调函数里面的this指向问题

js 复制代码
// 6.2
class Parent extends React.Component {
  state = {
    sonMsg: "",
  };
  getChildMsg = (msg) => {
    this.setState({
      sonMsg: msg,
    });
  };
  render() {
    return (
      <div>
        子组件传过来的值:{this.state.sonMsg}
        <Child getMsg={this.getChildMsg}></Child>
      </div>
    );
  }
}

class Child extends React.Component {
  state = {
    sonMsg: "react牛掰!!!",
  };
  sendMsg = () => {
    this.props.getMsg(this.state.sonMsg);
  };
  render() {
    return (
      <div>
        <button onClick={this.sendMsg}>向父组件传值</button>
      </div>
    );
  }
}

ReactDOM.createRoot(document.getElementById("root")).render(<Parent></Parent>);

6.3 兄弟传值

A. 将共享数据提升到最近的公共父组件中,由公共父组件管理这个状态

B. 思想:状态提升

C. 公共父组件职责:1.提供共享状态 2.提供操作共享状态的方法

D. 要通过的子组件只需通过props接收状态或操作状态的方法

js 复制代码
class Parent63 extends React.Component {
  // 提供公共状态
  state = {
    count: 0,
  };
  // 提供修改状态的方法
  onIncrement = () => {
    this.setState({
      count: this.state.count + 1,
    });
  };
  render() {
    return (
      <div>
        <Child631 count={this.state.count}></Child631>
        <Child632 onIncrement={this.onIncrement}></Child632>
      </div>
    );
  }
}
const Child631 = (props) => {
  return <div>计数器{props.count}</div>;
};
const Child632 = (props) => {
  return <button onClick={() => props.onIncrement()}>+1</button>;
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <Parent63></Parent63>
);

6.4 context

思考:App组件要传递数据给Child组件,该如何处理

A. 处理方式:使用props一层层组件往下传递(繁琐)

B. 更好的姿势:使用Context

C. 作用:跨组件传递数据(比如:主题、语言等)

6.4.1 使用步骤

A. 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件。

B.使用Provider组件作为父节点

C. 设置value属性,表示要传递的数据

D. 调用Consumer组件传递数据

css 复制代码
.app64{
background-color: red;
padding:20px;
}
.node64{
background-color: yellow;
padding:20px;
}
.subNode64{
background-color: green;
padding:20px;
}
.child64{
background-color: purple;
padding: 20px;
}
js 复制代码
// 创建context得到两个组件
const { Provider, Consumer } = React.createContext();

class App64 extends React.Component {
  render() {
    return (
      <div>
        <Provider value={"hello"}>
          <div className="app64">
            <Node64></Node64>
          </div>
        </Provider>
      </div>
    );
  }
}

const Node64 = () => {
  return (
    <div className="node64">
      <SubNode64></SubNode64>
    </div>
  );
};

const SubNode64 = () => {
  return (
    <div className="subNode64">
      <Child64></Child64>
    </div>
  );
};

const Child64 = () => {
  return (
    <div className="child64">
      <Consumer>{(data) => <span>我是子节点{data}</span>}</Consumer>
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<App64></App64>);

6.4.2 总结

A. 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯

B. Context提供了两个组件:Provider和Consumer

C. Provider组件:用来提供数据

D. Consumer组件:用来消费数据

7.props深入

7.1 children属性

A. children属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性

B. children属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)

js 复制代码
const App1 = (props) => {
  console.log(props);
  return (
    <div>
      <h1>组件标签的子节点:</h1>
      {props.children}
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <App1>
    <p>我是p标签</p>
    {/* {() => {
      console.log(123);
    }} */}
  </App1>
);

7.2 props校验

A. 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据

B. 如果传入的数据格式不对,可能会导致组件内部报错

C. 关键问题:组件的使用者不知道明确的错误原因

js 复制代码
function App72(props) {
  const list = props.list;
  const lis = list.map((item, index) => <li key={index}>{item}</li>);
  return <ul>{lis}</ul>;
}

ReactDOM.createRoot(document.getElementById("root")).render(
  <App72 list={19}></App72>
);

D.props校验:允许在创建组件的时候,就指定props的类型、格式等

E. 作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性

7.2.1 使用步骤

A. 安装包prop-types(yarn add prop-types/npm i prop-types)

B. 导入prop-types包

C. 使用组件名.propTypes={}来给组件的props添加校验规则

D. 校验规则通过PropTypes对象来指定

js 复制代码
import PropTypes from "prop-types";
function App721(props) {
  const list = props.list;
  const lis = list.map((item, index) => <li key={index}>{item}</li>);
  return <ul>{lis}</ul>;
}
App721.propTypes = {
  //约定list属性为array类型
  //如果类型不对,则报错明确错误,便于分析错误原因
  list: PropTypes.array,
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <App721 list={19}></App721>
);

7.3 props的默认值

A. 场景:分页组件->每页显示条数

B. 作用:给props设定默认值,在未传入props时生效

js 复制代码
class App73 extends React.Component {
  render() {
    return (
      <div>
        此处展示props默认值:{this.props.age} {this.props.gender}
      </div>
    );
  }
}

App73.defaultProps = {
  age: 18,
  gender: "男",
};

ReactDOM.createRoot(document.getElementById("root")).render(
  <App73 name="zs"></App73>
);

8.组件的生命周期

8.1 组件的生命周期概述

A. 意义:组件的生命周期有助于理解组件的运行方式,完成更复杂的组件功能,分析组件错误原因等

B. 组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程

C. 生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数。

D. 钩子函数的作用:为开发人员再不同阶段操作组件提供了时机

E. 只有类组件才有生命周期

8.2 生命周期的三个阶段

A. 每个阶段的执行时机

B. 每个阶段钩子函数的执行顺序

C. 每个阶段钩子函数的作用

8.2.1 创建时(挂载阶段)

A. 执行时机:组件创建时(页面加载时)

B. 执行顺序:
constructor:创建组件时,最先执行。初始化state和为事件处理程序绑定this

render:每次组件渲染都会触发,渲染ui(注意:不能调用setState)

componentDidMount:组件挂载(完成DOM渲染)后,发送网络请求,DOM操作

js 复制代码
class App821 extends React.Component {
  constructor(props) {
    super(props);
    // 初始化数据
    this.state = {
      count: 0,
    };
    // 主要处理this指向问题
    console.log("生命周期钩子函数:constructor");
    const title = document.getElementById("h1");
    console.log("constructor中的DOM为" + title); // null,获取不到DOM
  }
  // 进行DOM操作
  // 发送ajax请求,获取远程数据
  componentDidMount() {
    const title = document.getElementById("h1");
    console.log("componentDidMount中的DOM为" + title); // 获取到了
  }
  render() {
    // 不要在render中调用setState
    // this.setState({
    //   count: 1,
    // });
    console.log("生命周期钩子函数:render");
    return (
      <div>
        <h1 id="h1">统计豆豆被打的次数:</h1>
        <button>打豆豆</button>
      </div>
    );
  }
}

ReactDOM.createRoot(document.getElementById("root")).render(<App821></App821>);

8.2.2 更新时(更新阶段)

A. 执行时机:1.setState() 2.forceUpdate() 3.组件接收到新的props

B. 说明:以上三者任意一种变化,组件就会重新渲染

C. 执行顺序:

render():每次组件渲染都会触发,渲染UI(在挂载阶段是同一个render)

componentDidUpdate():组件更新(完成DOM渲染)后,发送网络请求,DOM操作(注意:如果要

setState()必须放在一个if条件中)

js 复制代码
class App822 extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }
  handleClick = () => {
    this.setState({
      count: this.state.count + 1,
    });
  };
  render() {
    return (
      <div>
        <Counter822 count={this.state.count}></Counter822>
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    );
  }
}

class Counter822 extends React.Component {
  render() {
    console.log("子组件-生命周期钩子函数:render()");
    return (
      <div>
        <h1 id="title">打豆豆的数量:{this.props.count}</h1>
      </div>
    );
  }
  componentDidUpdate(prevProps) {
    console.log("子组件-生命周期钩子函数:componentDidUpdate()");
    //获取dom
    const title = document.getElementById("title");
    console.log(title.innerHTML);

    console.log(
      "前一状态的props值:",
      prevProps,
      "当前状态的props值:",
      this.props
    );
  }
}

ReactDOM.createRoot(document.getElementById("root")).render(<App822></App822>);

8.2.3 卸载时

A.执行时机:组件从页面中消失

componentWillUnmount:组件卸载(从页面消失),执行清理工作

js 复制代码
class App823 extends React.Component {
  constructor(props) {
    super(props);
    // 初始化数据
    this.state = {
      count: 0,
    };
  }
  handleClick = () => {
    this.setState({
      count: this.state.count + 1,
    });
  };
  render() {
    console.log("生命周期钩子函数:render()");
    return (
      <div>
        {this.state.count > 3 ? (
          <h1>豆豆被打死了</h1>
        ) : (
          <Count823 count={this.state.count}></Count823>
        )}
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    );
  }
}
class Count823 extends React.Component {
  render() {
    console.log("子组件-生命周期钩子函数:render()");
    return <h1 id="title">豆豆被打的次数:{this.props.count}</h1>;
  }
  componentDidMount() {
    console.log("子组件-生命周期钩子函数:componentDidMount()"); // 创建时钩子函数,只在创建时执行一次
    this.timerId = setInterval(() => {
      console.log("定时器正在执行中~~~~~~~~");
    }, 500);
  }
  componentWillUnmount() {
    console.log("子组件-生命周期钩子函数:componentWillUnmount()");
    clearInterval(this.timerId);
  }
}

ReactDOM.createRoot(document.getElementById("root")).render(<App823></App823>);

9.render-props和高阶组件

9.1 react组件复用概述

A. 思考:如果两个组件中的部分功能相似或相同,该如何处理?

B. 处理方式:复用相似的功能(联想函数封装)

C. 复用什么?1.state 2.操作state的方法(组件状态逻辑)

D. 两种方式:1.render props模式 2.高阶组件(HOC)

E. 注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)

9.2 render props 模式

9.2.1 思路分析

A. 思路:将要复用的state和操作state的方法封装到一个组件中

B. 问题1:如何拿到该组件中复用的state

C. 在使用组件时,添加一个值为函数的prop,通过函数参数获取(需要组件内部实现)

D. 问题2:如何渲染任意的UI?

E. 使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)

9.2.2 使用步骤

A. 创建Mouse组件,在组件中提供复用的状态逻辑代码(1.状态 2.操作状态的方法)

B. 将要复用的状态作为props.render(state)方法的参数,暴露到组件外部

C. 使用props.render()的放回置作为要渲染的内容
9.2render_props.js

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

//创建Mouse组件
class Mouse extends React.Component {
  // 鼠标位置
  state = {
    x: 0,
    y: 0,
  };
  //鼠标移动事件的事件处理程序
  handleMouse = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    });
  };
  // 组件被挂载到页面后调用--监听鼠标移动事件
  componentDidMount() {
    window.addEventListener("mousemove", this.handleMouse);
  }
  render() {
    return this.props.render(this.state);
  }
}
class App92 extends React.Component {
  render() {
    return (
      <div>
        <h1>render props模式</h1>
        <Mouse
          render={(mouse) => {
            return (
              <h1>
                鼠标坐标:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        ></Mouse>
      </div>
    );
  }
}

export default App92;

index.js

js 复制代码
import App92 from "./92render_props";
ReactDOM.createRoot(document.getElementById("root")).render(<App92></App92>);

9.2.3 mouse组件的复用

A. Mouse组件负责:封装复用的状态逻辑代码(1.状态 2.操作状态的方法)

B. 状态:鼠标坐标(x,y)

C. 操作状态的方法:鼠标移动事件

D. 传入的render prop负责:使用复用的状态来渲染UI结构

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建Mouse组件
class Mouse extends React.Component {
  // 鼠标位置
  state = {
    x: 0,
    y: 0,
  };
  //鼠标移动事件的事件处理程序
  handleMouse = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    });
  };
  // 组件被挂载到页面后调用--监听鼠标移动事件
  componentDidMount() {
    window.addEventListener("mousemove", this.handleMouse);
  }
  render() {
    return this.props.render(this.state);
  }
}
class App92 extends React.Component {
  render() {
    return (
      <div>
        <h1>render props模式</h1>
        {/* 鼠标位置 */}
        <Mouse
          render={(mouse) => {
            return (
              <h1>
                鼠标坐标:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        ></Mouse>
        {/* 图片 */}
        <Mouse
          render={(mouse) => {
            return (
              <img
                width="50"
                src={img}
                alt="鼠标图片"
                style={{
                  position: "absolute",
                  top: mouse.y,
                  left: mouse.x,
                  cursor: "none",
                }}
              ></img>
            );
          }}
        ></Mouse>
      </div>
    );
  }
}

export default App92;

9.2.4 children代替render属性

A. 注意:并不是该模式叫render props就必须使用名为render的prop,实际上可以使用任意名称的prop

B. 把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式

C. 推荐:使用children代替render属性

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建Mouse组件
class Mouse extends React.Component {
  // 鼠标位置
  state = {
    x: 0,
    y: 0,
  };
  //鼠标移动事件的事件处理程序
  handleMouse = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    });
  };
  // 组件被挂载到页面后调用--监听鼠标移动事件
  componentDidMount() {
    window.addEventListener("mousemove", this.handleMouse);
  }
  render() {
    // return this.props.render(this.state);
    return this.props.children(this.state);
  }
}
class App92 extends React.Component {
  render() {
    return (
      <div>
        <h1>render props模式</h1>
        {/* 鼠标位置 */}
        {/* <Mouse
          render={(mouse) => {
            return (
              <h1>
                鼠标坐标:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        ></Mouse> */}
        {/* 图片 */}
        {/* <Mouse
          render={(mouse) => {
            return (
              <img
                width="50"
                src={img}
                alt="鼠标图片"
                style={{
                  position: "absolute",
                  top: mouse.y,
                  left: mouse.x,
                  cursor: "none",
                }}
              ></img>
            );
          }}
        ></Mouse> */}
        {/* children版本 */}
        <Mouse>
          {(mouse) => {
            return (
              <h1>
                鼠标位置:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        </Mouse>
        {/* Mouse组件复用 */}
        <Mouse>
          {(mouse) => {
            return (
              <img
                src={img}
                alt="react"
                width={100}
                style={{
                  position: "absolute",
                  top: mouse.y - 50,
                  left: mouse.x - 50,
                }}
              ></img>
            );
          }}
        </Mouse>
      </div>
    );
  }
}

export default App92;

9.2.5 代码优化

A. 推荐:给render props 模式添加props校验

B. 应该在组件卸载时接触mousemove事件绑定

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建Mouse组件
class Mouse extends React.Component {
  // 鼠标位置
  state = {
    x: 0,
    y: 0,
  };
  //鼠标移动事件的事件处理程序
  handleMouse = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY,
    });
  };
  // 组件被挂载到页面后调用--监听鼠标移动事件
  componentDidMount() {
    window.addEventListener("mousemove", this.handleMouse);
  }
  render() {
    // return this.props.render(this.state);
    return this.props.children(this.state);
  }
  // 推荐:在组件卸载时移除事件绑定
  componentWillUnmount() {
    window.removeEventListener("mousemove", this.handleMouse);
  }
}
// 添加校验
Mouse.propTypes = {
  children: PropTypes.func.isRequired,
};

class App92 extends React.Component {
  render() {
    return (
      <div>
        <h1>render props模式</h1>
        {/* 鼠标位置 */}
        {/* <Mouse
          render={(mouse) => {
            return (
              <h1>
                鼠标坐标:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        ></Mouse> */}
        {/* 图片 */}
        {/* <Mouse
          render={(mouse) => {
            return (
              <img
                width="50"
                src={img}
                alt="鼠标图片"
                style={{
                  position: "absolute",
                  top: mouse.y,
                  left: mouse.x,
                  cursor: "none",
                }}
              ></img>
            );
          }}
        ></Mouse> */}
        {/* children版本 */}
        <Mouse>
          {(mouse) => {
            return (
              <h1>
                鼠标位置:{mouse.x}:{mouse.y}
              </h1>
            );
          }}
        </Mouse>
        {/* Mouse组件复用 */}
        <Mouse>
          {(mouse) => {
            return (
              <img
                src={img}
                alt="react"
                width={100}
                style={{
                  position: "absolute",
                  top: mouse.y - 50,
                  left: mouse.x - 50,
                }}
              ></img>
            );
          }}
        </Mouse>
      </div>
    );
  }
}

export default App92;

9.3 高阶组件

9.3.1 概述

A. 目的:实现状态逻辑复用

B. 采用包装(装饰)模式,比如说:手机壳

C. 手机:获取保护功能

D. 手机壳:提供保护功能

E. 高阶组件就相当于手机壳,通过包装组件,增强组件功能

9.3.2 思路分析

A. 高阶组件(HOC,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件

B. 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传

递给被包装组件WrappendComponent

9.3.3 使用步骤

A. 创建一个函数,名称约定以with开头

B. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)

C. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回

D. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件

E. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
9.3 gjzj.js

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建高阶组件
function WithMouse(WrappendComponent) {
  //该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0,
    };
    handleMouse = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY,
      });
    };
    // 监听鼠标移动事件
    componentDidMount() {
      window.addEventListener("mousemove", this.handleMouse);
    }
    //推荐:在组件卸载时移除事件绑定
    componentWillUnmount() {
      window.removeEventListener("mousemove", this.handleMouse);
    }
    // 渲染被包装的组件,并传递复用的状态逻辑
    render() {
      return <WrappendComponent {...this.state}></WrappendComponent>;
    }
  }

  return Mouse;
}

// 普通测试
const Position = (props) => {
  return (
    <h1>
      【x:{props.x},y:{props.y}】
    </h1>
  );
};

const Img = (props) => {
  return (
    <img
      src={img}
      alt="图片"
      width={50}
      style={{ position: "absolute", top: props.y, left: props.x }}
    ></img>
  );
};

// 获取增强后组件
const MousePosition = WithMouse(Position);
const MouseImg = WithMouse(Img);

class App93 extends React.Component {
  render() {
    return (
      <div>
        高阶组件:
        <MousePosition></MousePosition>
        <MouseImg></MouseImg>
      </div>
    );
  }
}

export default App93;
js 复制代码
import App93 from "./93gjzj";
ReactDOM.createRoot(document.getElementById("root")).render(<App93></App93>);

9.3.4 设置displayName

A. 使用高阶组件存在的问题:得到的两个组件名称相同

B. 原因:默认情况下,React使用组件名称作为displayName

C. 解决方式:为高阶组件设置displayName便于调试时区分不同的组件

D. displayName的作用:用于设置调试信息(React Developer Tools信息)

E. 设置方式:

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建高阶组件
function WithMouse(WrappendComponent) {
  //该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0,
    };
    handleMouse = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY,
      });
    };
    // 监听鼠标移动事件
    componentDidMount() {
      window.addEventListener("mousemove", this.handleMouse);
    }
    //推荐:在组件卸载时移除事件绑定
    componentWillUnmount() {
      window.removeEventListener("mousemove", this.handleMouse);
    }
    // 渲染被包装的组件,并传递复用的状态逻辑
    render() {
      return <WrappendComponent {...this.state}></WrappendComponent>;
    }
  }
  //设置display
  Mouse.displayName = `WithMouse${getDisplayName(WrappendComponent)}`;
  return Mouse;
}
function getDisplayName(WrappendComponent) {
  return WrappendComponent.displayName || WrappendComponent.name || "Component";
}

// 普通测试
const Position = (props) => {
  return (
    <h1>
      【x:{props.x},y:{props.y}】
    </h1>
  );
};

const Img = (props) => {
  return (
    <img
      src={img}
      alt="图片"
      width={50}
      style={{ position: "absolute", top: props.y, left: props.x }}
    ></img>
  );
};

// 获取增强后组件
const MousePosition = WithMouse(Position);
const MouseImg = WithMouse(Img);

class App93 extends React.Component {
  render() {
    return (
      <div>
        高阶组件:
        <MousePosition></MousePosition>
        <MouseImg></MouseImg>
      </div>
    );
  }
}

export default App93;

9.3.5 传递props

A. 问题:props丢失

B. 原因:高阶组件没有往下传递props

C. 解决方式:渲染WrappedComponent时,将state和this.props一起传递给组件

D. 传递方式:

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import PropTypes from "prop-types";

// 导入图片
import img from "./logo192.png";

//创建高阶组件
function WithMouse(WrappendComponent) {
  //该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0,
    };
    handleMouse = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY,
      });
    };
    // 监听鼠标移动事件
    componentDidMount() {
      window.addEventListener("mousemove", this.handleMouse);
    }
    //推荐:在组件卸载时移除事件绑定
    componentWillUnmount() {
      window.removeEventListener("mousemove", this.handleMouse);
    }
    // 渲染被包装的组件,并传递复用的状态逻辑
    render() {
      return (
        <WrappendComponent {...this.state} {...this.props}></WrappendComponent>
      );
    }
  }
  //设置display
  Mouse.displayName = `WithMouse${getDisplayName(WrappendComponent)}`;
  return Mouse;
}
function getDisplayName(WrappendComponent) {
  return WrappendComponent.displayName || WrappendComponent.name || "Component";
}

// 普通测试
const Position = (props) => {
  return (
    <h1>
      【x:{props.x},y:{props.y} {console.log(props)}】
    </h1>
  );
};

const Img = (props) => {
  return (
    <img
      src={img}
      alt="图片"
      width={50}
      style={{ position: "absolute", top: props.y, left: props.x }}
    ></img>
  );
};

// 获取增强后组件
const MousePosition = WithMouse(Position);
const MouseImg = WithMouse(Img);

class App93 extends React.Component {
  render() {
    return (
      <div>
        高阶组件:
        <MousePosition></MousePosition>
        <MouseImg></MouseImg>
      </div>
    );
  }
}

export default App93;
相关推荐
_.Switch31 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光35 分钟前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   36 分钟前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   37 分钟前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常3 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js