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;
相关推荐
_AaronWong21 小时前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode21 小时前
I/O 多路复用:从浏览器到 Linux 内核
前端
用户54330814419421 小时前
AI 时代,前端逆向的门槛已经低到离谱 — 以 Upwork 为例
前端
JarvanMo21 小时前
Flutter 版本的 material_ui 已经上架 pub.dev 啦!快来抢先体验吧。
前端
JohnYan21 小时前
工作笔记-CodeBuddy应用探索
javascript·ai编程·aiops
恋猫de小郭21 小时前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木21 小时前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮1 天前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati1 天前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉1 天前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain