❤React体系07-reder-props和高阶组件介绍
❤React体系07-reder-props和高阶组件介绍
1、组件复用的两种方式 (利用React自身特点演化成的固定模式)
render-props和高阶组件模式
(1)render-props模式
将复用的state和操作state的方法封装到一个组件中
思路分析 思路:将要复用的state和操作stae的方法封装到一个组件中
问题1:如何拿到该组件中复用的state
在使用组件时,添加一个值为函数的prop,通过函数参数 来获取(需要组件内部实现 )
问题2:如何渲染任意的UI?
使用该函数的返回值作为要渲染的UI内容
<Mouse render={(mouse)=>{
}}></Mouse>
<Mouse render={(mouse)=>{
<p>鼠标当前位置: x: {mouse.x} y: {mouse.y}</p>
}}></Mouse>
(2)高阶组件
高阶组件( higher-order component ,HOC )是 React 中复用组件逻辑的一种进阶技巧。它本身并不是 React 的 API,而是一种 React 组件的设计理念,众多的 React 库已经证明了它的价值,例如耳熟能详的 react-redux。
高阶组件的概念其实并不难,我们能通过类比高阶函数迅速掌握。高阶函数是把函数作为参数传入到函数中并返回一个新的函数。这里我们把函数替换为组件,就是高阶组件了。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
2、reder-props 模式的使用
(1)简单介绍
reder-props 模式是React之中一种将组件逻辑与 UI 展示分离的思想,从这个理解上我们可以看出,这肯定是将组件和逻辑进行抽离,本质上就是复用
呗
简单归纳一下可以分为:
组件的状态管理( reducer 函数处理)
组件传递状态和操作函数(props控制)
先来手写一个增减看看我们如何使用这种reder-props 模式
增减组件
import React, { useReducer } from 'react';
// 定义 reducer 函数
const counterReducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};
const Counter = () => {
// 创建状态和 dispatch 函数
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>增夹</button>
<button onClick={() => dispatch({ type: 'decrement' })}>D减少</button>
</div>
);
};
export default Counter;
总结:
上述过程中我们利用Counter(父组件拟定为)使用 useReducer 钩子函数来创建一个状态和 dispatch 函数,然后将这个状态和 dispatch 函数通过 props 传递给子组件(也就是counterReducer) 通过 props 传递的状态进行对应的操作,从而实现组件的增减和状态管理!
鼠标移动事件组件
接下来手写一个render-props 鼠标移动事件的组件
import React from 'react';
// render props 模式
class Mouse extends React.Component{
// 鼠标位置state
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 Appps extends React.Component{
render() {
return (
<div>
<h1 >render props模式</h1>
<Mouse render={(mouse)=>{
return (<p>鼠标位置x:{mouse.x}鼠标位置y:{mouse.y}</p>)
}}></Mouse>
</div>
)
}
}
export default Appps
(2) 复用组件
使用过程
演示Mouse组件的复用
Mouse组件负责:封装复用的状态逻辑代码(1.状态 2.操作状态的方法)
状态:鼠标坐标(xy)
操作状态的方法:鼠标移动事件
传入的render prop负责:使用复用的状态来渲染UI结构
class Mouse extends React.Component {
render(){
return this.props.render(this.state)
}
}
<Mouse render={(mouse)=><p>鼠标位置:{mouse.x},{mouse.y}</p>}></Mouse>
- 案例源码
// 01 导入图片
import img from './images/cat.png'
// 02复用组件
<Mouse render={(mouse)=>{
return (<div><img src={img} alt="猫咪" style={{
position:'absolute',
top:mouse.y-5,
left:mouse.x-5
}}/></div>)
}}></Mouse>
(3)推荐写法
使用children的写法
还是上面的增减,我们可以先写一个demo看看如何写
import React from 'react';
// 父组件
const ParentComponent = () => {
return (
<div>
<h2>Parent Component</h2>
{/* 使用 children 属性传递一个函数 */}
<ChildComponent>
{({ count, increment, decrement }) => (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)}
</ChildComponent>
</div>
);
};
// 子组件
const ChildComponent = ({ children }) => {
const [count, setCount] = React.useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
// 调用 children 函数并传递参数
return children({ count, increment, decrement });
};
export default ParentComponent;
父组件将一个函数作为 ChildComponent 的子元素来传递数据和操作函数。
子组件在内部调用这个函数并传递了当前的 count 值以及两个操作函数 increment 和 decrement。
从而实现了在 React 中使用 children
属性来实现 render props 模式的效果(如果你想用的话尽量肯定推荐你写这种!
)
联想到我们平时使用的一些UI自己写的组件库,这个复用性大大提高啊!
改良鼠标移入移出组件
<Mouse>
{mouse=>{
return (<p>鼠标位置x:{mouse.x}鼠标位置y:{mouse.y}</p>)
}}
</Mouse>
<Mouse>
{mouse=>{
return (<div><img src={img} alt="猫咪" style={{
position:'absolute',
top:mouse.y-5,
left:mouse.x-5
}}/></div>)
}}
</Mouse>
(4) 代码优化
① 与组件同级别给组件添加校验
// 01 导入
import ProtoTypes from 'prop-types';
// 02 添加校验
Mouse.protoTypes={
children:ProtoTypes.func.isRequired
}
缺失情况下:
报错:
(2)优化部分
1.添加props校验
2.组件卸载时候移除mousemove事间绑定
Musee.propTypes={shildern:PropTypes.cunc.isRequired}
componentWillUnmounted(){
windows.removeEventListener('mousemove',this.handleMuoseMove)
}
② 在组件解绑时候应该进行移除
class Mouse extends React.Component{
// 鼠标位置state
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 this.props.children(this.state);
}
}
3、高阶组件
(1) 什么是高阶组件
高阶组件是一个函数。传递给它一个组件,返回一个新的组件。
官方定义:
高阶组件的英文是 Higher-Order Components,简称为 HOC;
官方的定义: 高阶组件是一个
参数为组件
,并且返回值为新组件
的函数
;
const EnhancedComponent = enhance( MyComponent )
enhance()
这个函数就是我们的高阶组件,对MyComponent做出各种处理
高阶函数
高阶函数: 如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等
(2) 优势
- 代码复用:逻辑封装和重复代码的使用
- 状态提升:多个组件共享状态提升到更高层级的组件中,并通过 props 将状态传递给需要的子组件。
- 渲染劫持:劫持组件渲染过程,对组件的 props 进行一些转换或过滤,比如我们项目常见的一些对于年龄的判断。
- 访问 Refs:访问传入的组件的 refs,对其进行操作。
- 控制组件生命周期:不修改原始组件代码的情况下控制组件的生命周期。
(3) 高阶组件实践
比如常见的菜单是否展开:
import React from 'react';
// 高阶组件
function withAuth(Component) {
return function(props) {
const isMenushow = true; // 假设菜单已展示
return <Component {...props} isMenushow={isMenushow} />;
};
}
// 使用高阶组件
function MyComponent(props) {
return (
<div>
{props.isMenushow ? '菜单已展示' : '菜单未展示'}
</div>
);
}
const EnhancedComponent = withAuth(MyComponent);
// 渲染增强后的组件
function App() {
return (
<div>
<EnhancedComponent />
</div>
);
}
export default App;
分析:
高阶组件是一个函数,接受要包装的组件,返回包装以后的组件。
内部创建一个类组件,之后在其中提供复用的状态以及代码,通过props将复用的状态传递给被包装的组件WrappedComponenet
(4)高阶组件使用
写一个简单的一个高阶组件
import React from 'react';
// 高阶组件
const withLogging = (WrappedComponent) => {
// 返回一个新的组件
return class WithLogging extends React.Component {
componentDidMount() {
console.log('Component is mounted');
}
componentWillUnmount() {
console.log('Component is unmounted');
}
render() {
// 渲染被包裹的组件,并将所有传递给高阶组件的 props 传递给它
return <WrappedComponent {...this.props} />;
}
};
};
// 被包裹的组件
const MyComponent = ({ message }) => {
return <div>{message}</div>;
};
// 使用高阶组件增强 MyComponent
const MyComponentWithLogging = withLogging(MyComponent);
// 使用增强后的组件
const App = () => {
return <MyComponentWithLogging message="Hello, World!" />;
};
export default App;
更多高阶函数在后续之中进行不断完善!
转载自:https://juejin.cn/post/7362084371058114560