做一个网站要怎么做,wordpress主题修改应用,山东建设监理协会继续教育网站,申请主机网站在23年的时候#xff0c;我主要使用的框架还是Vue#xff0c;当时写了一篇“如何二次封装一个Vue3组件库#xff1f;”的文章#xff0c;里面涉及了一些如何使用Vue透传组件能力的方法。在我24年接触React之后#xff0c;我发现这种扩展组件能力的方式有一个专门的术语我主要使用的框架还是Vue当时写了一篇“如何二次封装一个Vue3组件库”的文章里面涉及了一些如何使用Vue透传组件能力的方法。在我24年接触React之后我发现这种扩展组件能力的方式有一个专门的术语高阶组件HOC。但在Vue开发中这个词很少听到。
这篇文章中会描述使用React透传组件各类能力的方式。这些透传方式经常在高阶组件中使用但并不只有高阶组件会用到它们。React有类式组件和函数式组件两种我们会分别介绍。
问题描述
首先我们列举下简单的场景说明我们为什么需要透传组件能力。这里以函数式组件为例。
// 问题示例这段代码是不正确的
import { useRef } from react;function FunComp() {return input /;
}function FunComp2() {return divinput //div;
}function App() {const refFun useRef(null);const handleClick () {console.log(click);};const handleClickFocus () {if (refFun.current) refFun.current?.focus();};return (divFunCompref{refFun}style{{ background: red }}onClick{handleClick}/div onClick{handleClickFocus}点我聚焦/div/div);
}假设我们想创建一个自定义组件FunComp里面封装了另外一个组件例如这里的input希望使用这个自定义组件增强原组件的能力或者预先设定一些样式等等自定义组件可能直接返回该组件也可能被处理过例如FunComp2被div包裹。
虽然原组件被封装了但是还希望原组件的能力直接被透传给自定义组件。例如我们在自定义组件上操作props, 事件ref等希望就像操作原组件一样。在Vue3中很多能力可以直接使用Attributes继承特性但是React却没有需要我们自己实现。
函数式组件-透传Props和事件
在函数式组件中prop实际上就是组件的入参且所有prop是被包含在同一个参数中的因此很容易透传给子组件。且事件本身实际上也是prop可以一并透传。
function FunComp(props) {return input {...props} /;
}function App() {const handleClick () {console.log(click);};return (divFunComp style{{ background: red }} onClick{handleClick} //div);
}使用{...props}可以实现透传Props和事件。上述例子中子组件可以接收到样式属性和事件。
函数式组件-透传子节点
React中有一个特殊的属性children表示父组件中包含的子节点。这也是需要透传的。
直接渲染children属性
function FunComp(props) {return div左 {props.children} 右/div;
}function App() {return (divFunComp子节点/FunCompFunComp //div);
}可以看到直接渲染props.children即可透传子节点。即使没有子节点这种透传也是没问题的。如果子组件本身已经透传了props透传的对象又是
使用透传Props实现透传子节点
既然children也是Props之一那么直接使用透传Props的方法是否可以呢? 我们试一下。
function FunComp(props) {return div左 {props.children} 右/div;
}function FunComp1(props) {return div {...props} /;
}function FunComp2(props) {return FunComp {...props} /;
}function App() {return (divFunComp1子节点/FunComp1FunComp1 children子节点 /FunComp2子节点/FunComp2FunComp2 children子节点 //div);
}/* 页面效果
子节点
子节点
左 子节点 右
左 子节点 右
*/FunComp1包含的子组件是一个非自定义组件divFunComp2包含的时自定义组件FunComp可以看到我们使用{...props}进行Props透传children实际上都被成功渲染了甚至对父组件直接设置children属性也可以。
冲突场景
既然Props透传即可实现那我们为什么还要强调一遍直接渲染props.children呢因为有时候子组件不只渲染children还有其它内容。如果Props和直接设置的子节点冲突那么还是直接设置的子节点优先级更高。
function FunComp(props) {return div {...props}左 {props.children} 右/div;
}function App() {return (divFunComp子节点/FunCompFunComp children子节点 //div);
}/* 页面效果
左 子节点 右
左 子节点 右
*/我们同时透传了Props也直接设置了子节点其中包含其它内容最后直接设置的子元素生效了。
函数式组件-透传ref
使用ref可以操作访问DOM节点获取DOM元素上的属性或者方法。ref也是可以透传的。
透传全部属性
import { forwardRef, useRef } from react;const FunComp forwardRef(function (props, ref) {return input ref{ref} /;
});function App() {const inputRef useRef(null);function handleClick() {console.log(inputRef.current?.style);inputRef.current?.focus();}return (divFunComp ref{inputRef} /div onClick{handleClick}点击聚焦/div/div);
}使用forwardRef可以透传ref属性。我们尝试了聚焦输入框以及console输出style属性都是正常生效的。
仅暴露部分属性
有时候我们不想暴露全部属性仅希望暴露我们希望用户使用的部分属性使用useImperativeHandle可以做到。
import { forwardRef, useRef, useImperativeHandle } from react;const FunComp forwardRef(function (props, ref) {const inputRef useRef(null);useImperativeHandle(ref, () {return {focus() {inputRef.current?.focus();},};});return input ref{inputRef} /;
});function App() {const inputRef useRef(null);function handleClick() {// 无法输出console.log(inputRef.current?.style);inputRef.current?.focus();}return (divFunComp ref{inputRef} /div onClick{handleClick}点击聚焦/div/div);
}我们仅向外层的ref暴露了focus因此外层组件focus可以正常调用但是却拿不到style属性了。使用这种形式还可以对方法进行额外的包装或者创建一些新的ref方法。
在React19中不再需要forwardRef了ref直接作为一个prop属性访问。可以看最后的参考文档。
类式组件-透传Props和事件
类式组件是另一种创建React组件的方法被React标记为过时的API但是在老代码中还经常被使用到。我们先来看一下在类式组件中如何Props和事件。
import { Component } from react;
class ClassComp extends Component {render() {return div {...this.props}你好/div;}
}class App extends Component {render() {const handleClick () {console.log(click);};return ClassComp style{{ background: red }} onClick{handleClick} /;}
}通过上述代码可以看到在类式组件中透传Props和事件与函数式组件一致使用{...props}可以实现透传Props和事件。
类式组件-透传子节点
来看看类式组件是如何透传子节点的。
直接渲染children属性
import { Component } from react;
class ClassComp extends Component {render() {return div左 {this.props.children} 右/div;}
}class App extends Component {render() {return (divClassComp子节点/ClassCompClassComp //div);}
}代码依然与类式组件基本一致直接渲染{this.props.children}即可。
使用透传Props实现透传子节点
上一节讲到的透传Props同样可以实现透传子节点。
import { Component } from react;
class ClassComp extends Component {render() {return div左 {this.props.children} 右/div;}
}
class ClassComp1 extends Component {render() {return div {...this.props} /;}
}
class ClassComp2 extends Component {render() {return ClassComp {...this.props} /;}
}class App extends Component {render() {return (divClassComp1子节点/ClassComp1ClassComp1 children子节点 /ClassComp2子节点/ClassComp2ClassComp2 children子节点 //div);}
}/* 页面效果
子节点
子节点
左 子节点 右
左 子节点 右
*/与函数式组件一致Props透传时也会透传children甚至对父组件直接设置children属性也可以透传。至于Props和直接设置的子节点冲突的场景也与函数式组件一致这里就不举例了。
类式组件-透传ref
类式组件透传Ref的形式就与函数式组件不同了。具体类式组件有不同的实现方式我们分别介绍下
暴露部分属性
import { Component, createRef } from react;class ClassComp extends Component {inputRef createRef();focus() {this.inputRef.current?.focus();}render() {return input ref{this.inputRef} /;}
}class App extends Component {classRef createRef();handleClick() {console.log(this.classRef.current);this.classRef.current?.focus();}render() {return (divClassComp ref{this.classRef} /div onClick{() this.handleClick()}点击聚焦/div/div);}
}通过代码可以看到在类式组件中不需要通过forwardRef等方法就可以使用ref访问子组件且能执行子组件类中的方法。所以我们只要把需要暴露的内容包装成一个方法那么就可以让父组件获取到。 通过输出的图可以看到不仅能拿到方法还能拿到属性和其它很多东西。
拿到子组件内部的ref
既然可以拿到子组件类中和属性也能拿到。那么父组件可以直接拿到子组件内部的ref属性inputRef父组件可以直接拿到它来执行内部的方法。
import { Component, createRef } from react;class ClassComp extends Component {inputRef createRef();render() {return input ref{this.inputRef} /;}
}class App extends Component {classRef createRef();handleClick() {this.classRef.current?.inputRef?.current?.focus();}render() {return (divClassComp ref{this.classRef} /div onClick{() this.handleClick()}点击聚焦/div/div);}
}通过代码可以看到子组件不需要透出方法了父组件直接拿到子组件的inputRef想执行什么就执行什么做到了真正的“透传”。绑定ref还有另一种方式这里也介绍一下
import { Component, createRef } from react;class ClassComp extends Component {render() {return input refinputRef /;}
}class App extends Component {classRef createRef();handleClick() {this.classRef.current?.refs?.inputRef?.focus();}render() {return (divClassComp ref{this.classRef} /div onClick{() this.handleClick()}点击聚焦/div/div);}
}ref属性的值可以直接是一个字符串通过this.refs可以拿到使用字符串形式绑定的ref。
总结
函数式组件与类式组件在Props和事件透传的方式基本一致但是ref透传的区别较大。直接对比的话好像类式组件的透传能力更强一些但是它把组件内部所有内容全暴露在外违反了封装的原则子组件内部的改动很容易影响父组件不是一个好的设计。
在React19版本中ref属性也变成了prop仅通过透传Props就能实现透传组件大部分能力了。
参考
如何二次封装一个Vue3组件库 https://jzplp.github.io/2023/component-lib.htmlVue3 透传Attributes https://cn.vuejs.org/guide/components/attrsReact 使用ref操作DOM https://zh-hans.react.dev/learn/manipulating-the-dom-with-refsReact omponent https://zh-hans.react.dev/reference/react/Componentref用法 https://blog.csdn.net/qq_47305413/article/details/136059266React v19 ref作为一个属性 https://zh-hans.react.dev/blog/2024/12/05/react-19#ref-as-a-prop