hook封装示例

下面我们再来封装一个步骤条的 hook

在这里插入图片描述

原代码

import { Steps, Button, message } from 'antd';

const { Step } = Steps;

const steps = [
  {
    title: 'First',
    content: 'First-content',
  },
  {
    title: 'Second',
    content: 'Second-content',
  },
  {
    title: 'Last',
    content: 'Last-content',
  },
];

const App = () => {
  const [current, setCurrent] = React.useState(0);

  const next = () => {
    setCurrent(current + 1);
  };

  const prev = () => {
    setCurrent(current - 1);
  };

  return (
    <>
      <Steps current={current}>
        {steps.map((item) => (
          <Step key={item.title} title={item.title} />
        ))}
      </Steps>
      <div className="steps-content">{steps[current].content}</div>
      <div className="steps-action">
        {current < steps.length - 1 && (
          <Button type="primary" onClick={() => next()}>
            Next
          </Button>
        )}
        {current === steps.length - 1 && (
          <Button type="primary" onClick={() => message.success('Processing complete!')}>
            Done
          </Button>
        )}
        {current > 0 && (
          <Button style={{ margin: '0 8px' }} onClick={() => prev()}>
            Previous
          </Button>
        )}
      </div>
    </>
  );
};

下面我们来一步步实现

import React, { useCallback, useState, useEffect } from 'react';
import { Steps } from 'antd';
const { Step } = Steps;

// 这里我们先实现一个包裹step容器,通过上级传来的children与currentStep来判断显示对应的steps-content即显示内容区域
const StepContainer = (props) => {
  const [stepContent, setStepContent] = useState([]);
  const [stepNameList, setStepNameList] = useState([]);
  const { children, currentStep } = props;

  // 当children发生变化的时候更新步骤条
  const initStepNameList = useCallback(() => {
    const PropsStepNameList = React.Children.map(children, (child, index) => {
      return {
        title: child.props.title,
        key: child.props.key || `childStep${index}`,
      };
    });
    setStepNameList(PropsStepNameList);
  }, [children]);

  // 在上级currentStep当前步数与children判断显示对应的steps-content
  const initStepContent = useCallback(() => {
    const propsStepContent = React.Children.map(children, (child, index) => {
      if (index === currentStep) {
        return {
          ...child,
        };
      }
      return null;
    });
    setStepContent(propsStepContent);
  }, [currentStep, children]);

  // 钩子 发生变化 且初始化时执行
  useEffect(() => {
    initStepNameList();
    initStepContent();
  }, [initStepNameList, initStepContent]);

  // 显示对应内容
  return (
    <>
      <Steps current={currentStep}>
        {stepNameList.map((step) => (
          <Step key={step.key} title={step.title} />
        ))}
      </Steps>
      {stepContent}
    </>
  );
};

// 引用
const App = () => {
  const [currentStep, setCurrentStep] = useState(0);
  const changeStep = (stepNum) => {
    setCurrentStep(stepNum);
  };
  return (
    <StepContainer currentStep={currentStep}>
      <span title="First">First-content</span>
      <span title="Second">Second-content</span>
      <span title="Last">Last-content</span>
    </StepContainer>
  );
};

上述例子中,我们把处理 stepNameList、stepContent 的操作,都放在了 StepContainer hook 中;
我们还可以把复杂操作,通过 hooks 提取出去;将组件中关联部分拆分;

import React, { useCallback, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Card, Steps } from 'antd';
import useStep from '@/hooks/useStep';
const { Step } = Steps;
const useStep = (props) => {
  const [stepContent, setStepContent] = useState([]);
  const [stepNameList, setStepNameList] = useState([]);
  const { children, currentStep } = props;
  // 当children发生变化的时候更新步骤条
  const initStepNameList = useCallback(() => {
    const PropsStepNameList = React.Children.map(children, (child, index) => {
      return {
        title: child.props.title,
        key: child.props.key || `childStep${index}`,
      };
    });
    setStepNameList(PropsStepNameList);
  }, [children]);

  // 在上级currentStep当前步数与children判断显示对应的steps-content
  const initStepContent = useCallback(() => {
    const propsStepContent = React.Children.map(children, (child, index) => {
      if (index === currentStep) {
        return {
          ...child,
        };
      }
      return null;
    });
    setStepContent(propsStepContent);
  }, [currentStep, children]);

  // 钩子
  useEffect(() => {
    initStepNameList();
    initStepContent();
  }, [initStepNameList, initStepContent]);
  return [currentStep, stepContent, stepNameList];
};

// 步骤条容器 在这里直接使用useStep 去判断显示对应内容
const StepContainer = (props) => {
  const [currentStep, stepContent, stepNameList] = useStep(props);
  return (
    <>
      <Steps current={currentStep}>
        {stepNameList.map((step) => (
          <Step key={step.key} title={step.title} />
        ))}
      </Steps>
      {stepContent}
    </>
  );
};

// 引用
const App = () => {
  const [currentStep, setCurrentStep] = useState(0);
  const changeStep = (stepNum) => {
    setCurrentStep(stepNum);
  };
  return (
    <StepContainer currentStep={currentStep}>
      <span title="First">First-content</span>
      <span title="Second">Second-content</span>
      <span title="Last">Last-content</span>
    </StepContainer>
  );
};

我们在上述 StepContainer 组件中再次将它的副作用操作拆分;在此组件中只关注渲染结果,useStep 劫持 props,返回一个数组,数组中包括 currentStep, stepContent, stepNameList;在我们使用 useStep 时,会根据我们 props 的不同而返回不同的状态

下面我们先来实现一个 loading 的 hook

TestCount 的需求为点击增加,显示 loading,两秒后显示为增加后的数值;点击减少,显示 loading,两秒后显示为减少后的数值
在这里插入图片描述

import React, { useState, useEffect } from 'react';

const AsyncCount = ({ countNum }) => {
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(0);
  // 当传下来的countNum 发生变化的时候 我们回去更改自身组件的count 与loading状态
  useEffect(() => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setCount(countNum);
    }, 2000);
  }, [countNum]);
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};

// 在此组件中使用AsyncCount组件
const TestCount = () => {
  const [count, setCount] = useState(0);
  const changeCount = (name) => {
    setCount(name);
  };
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1);
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1);
        }}
      >
        减少
      </button>
    </>
  );
};

export default TestCount;

再上述例子中,我们把处理 count 异步的操作以及是否渲染 loading,都放在了 AsyncCount hook 中;
我们还可以把复杂操作,通过 hooks 提取出去;将组件中关联部分拆分;

那下面我们在做一个更加细化的拆分,拆出一个自己的 hook

import React, { useState, useEffect } from 'react';
// 我们把此处变化count与loading的hook拆出来
const useCount = (countNum) => {
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(0);
  // 在传入进来countNum发生变化的时候,重新return 出去新的loading与count
  useEffect(() => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setCount(countNum);
    }, 2000);
  }, [countNum]);
  return [loading, count];
};

// 此组件中只拿到countNum 使用useCount hook 去除对应的loading与count
const AsyncCount = ({ countNum }) => {
  const [loading, count] = useCount(countNum);
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};

// 此组件中使用AsyncCount组件
const TestCount = () => {
  const [count, setCount] = useState(0);
  const changeCount = (count) => {
    setCount(count);
  };
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1);
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1);
        }}
      >
        减少
      </button>
    </>
  );
};

export default TestCount;

我们在上述 AsyncCount 组件中再次将它的副作用操作拆分;在此组件中只关注渲染结果,useCount 接受一个数字,返回一个数组,数组中包括状态,与 count 两个结果;在我们使用 useCount 时,会根据我们传入参数的不同而返回不同的状态;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值