首页 > 解决方案 > 使用 React 钩子重构功能组件

问题描述

我有几个共享相同逻辑的功能组件。所以我想用 React hooks 重构它们。他们都在挂载时向服务器发出一些调用,以检查订单是否已支付。如果是,paid则 state 设置为 true ,并且正在下载文件。在提交时,我检查paid状态是否设置为 true,如果是,则正在下载相同的文件,如果不是,则创建新订单并将用户重定向到带有付款表单的页面。

我已经提取了所有对服务器进行 API 调用的函数( getOrder()getPaymentState()createOrder()和)。如何进一步优化此代码,以便我可以将、和移到组件之外,以便对其他组件也使用相同的逻辑?initPayment()downloadFile()checkOrder()checkPayment()downloadPDF()newOrder()

这是我的组件:

const Form = () => {
  const [paid, setPaid] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState({});

  const checkOrder = async () => {
    let search = new URLSearchParams(window.location.search);
    let success = search.get("Success");
    if (success) {
      try {
        const data = await getOrder();
        setData(data);
        checkPayment(data);
      } catch (err) {
        alert(err.message)
      }
    }
  };

  const checkPayment = async values => {
    try {
      const paid = await getPaymentState();
      setPaid(paid);
      downloadPDF(values);
    } catch (err) {
      alert(err.message)
    }
  };

  const downloadPDF = async values => {
    setLoading(true);
    let downloadData = {
      email: values.email,
      phone: values.phone
    }
    const response = await downloadFile(downloadData, sendURL);
    setLoading(false);
    window.location.assign(response.pdf);
  }

  const newOrder = async values => {
    setSubmitting(true);
    const order = await createOrder(values, description, sum);
    const paymentUrl = await initPayment(order, description, sum, returnURL);
    setSubmitting(false);
    window.location.assign(paymentUrl);
  }

  const onSubmit = async values => {
    if (paid) {
      try {
        downloadPDF(data);
      } catch (err) {
        console.log(err);
      }
    } else {
      try {
        newOrder(values)
      } catch (err) {
        alert(err.message)
      }
    }
  };

  useEffect(() => {
    checkOrder();
  }, []);
  return (

  )
}

编辑 1:我还需要能够将一些数据传递给这个钩子:downloadDatasendURL、和description,这在每种情况下都会有所不同。然后需要填充来自.sumreturnURLdownloadDatavalues

如果您能指出正确的方向,我将不胜感激。我只是在学习 React,我真的很想找到正确的方法来做到这一点。

编辑2:我已经根据以前的答案发布了我自己的答案和工作代码。这不是最终的,因为我仍然需要移出downloadPDF()组件并传递downloadData给它,但是当我这样做时,我收到一个错误,即值未定义。如果有人可以帮助我,我会接受它作为答案。

标签: reactjs

解决方案


您可以将 Form 组件中的所有通用内容提取到自定义 Hook 中,并从此挂钩返回所需的值

依赖项的值会根据被调用的组件而变化,可以作为参数传递给钩子。该钩子还可以返回一个 onSubmit 函数,您可以将 downloadData 传递给该函数

const useOrderHook = ({returnURL, sendURL, }) => {
  const [paid, setPaid] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState({});

  const checkOrder = async () => {
    let search = new URLSearchParams(window.location.search);
    let success = search.get("Success");
    if (success) {
      try {
        const data = await getOrder();
        setData(data);
        checkPayment(data);
      } catch (err) {
        alert(err.message)
      }
    }
  };

  const checkPayment = async values => {
    try {
      const paid = await getPaymentState();
      setPaid(paid);
      downloadPDF(values);
    } catch (err) {
      alert(err.message)
    }
  };

  const downloadPDF = async values => {
    setLoading(true);
    let downloadData = {
      email: values.email,
      phone: values.phone
    }
    const response = await downloadFile(downloadData, sendURL);
    setLoading(false);
    window.location.assign(response.pdf);
  }

  const newOrder = async (values, description, sum) => {
    setSubmitting(true);
    const order = await createOrder(values, description, sum);
    const paymentUrl = await initPayment(order, description, sum, returnURL);
    setSubmitting(false);
    window.location.assign(paymentUrl);
  }

  const onSubmit = async ({values, downloadData: data, description, sum}) => {
    if (paid) {
      try {
        downloadPDF(data);
      } catch (err) {
        console.log(err);
      }
    } else {
      try {
        newOrder(values, description, sum)
      } catch (err) {
        alert(err.message)
      }
    }
  };

  useEffect(() => {
    checkOrder();
  }, []);

  return {onSubmit, loading, submitting, paid, data };
}

现在您可以在像 Form 这样的组件中使用这个钩子,如下所示

const Form = () => {
    const {onSubmit, newOrder, loading, submitting, paid, data } = useOrderHook({returnUrl: 'someUrl', sendURL: 'Some send URL'})

    const handleSubmit = (values) => {
        // since this function is called, you can get the values from its closure.
        const data = {email: values.email, phone: values.phone}
        onSubmit({ data, values, description, sum})// pass in the required values for onSubmit here. you can do the same when you actually call newOrder from somewhere
    }
    // this is how you pass on handleSubmit to React-final-form
    return <Form
      onSubmit={handleSubmit }
      render={({ handleSubmit }) => {
        return <form onSubmit={handleSubmit}>...fields go here...</form>
      }}
    />
}

推荐阅读