使用React挂钩重构功能组件

时间:2020-04-15 19:10:36

标签: reactjs

我有几个共享相同逻辑的功能组件。所以我想使用React钩子重构它们。他们所有人都对挂载的服务器进行一些调用,以检查订单是否已付款。如果是,则paid状态设置为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 :我还需要能够将一些数据传递到此钩子:downloadDatasendURLdescriptionsumreturnURL,在每种情况下都会有所不同。然后需要downloadData填充values中的一些数据。

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

编辑2 :我已经根据先前的答案将own answer与工作代码一起发布了。这不是最终的,因为我仍然需要将downloadPDF()移到组件外部并将downloadData传递给它,但是这样做时,我得到一个错误,那就是值是不确定的。如果有人可以帮助我,我将接受它作为答案。

3 个答案:

答案 0 :(得分:1)

我对代码进行了快速重构,并将其放在自定义的挂钩中,看起来搜索参数是何时需要运行效果的关键。

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

  const checkOrder = useCallback(async () => {
    let paramSearch = new URLSearchParams(search);
    let success = paramSearch.get('Success');
    if (success) {
      try {
        //why not just pass it, makes getOrder a little less impure
        const data = await getOrder(paramSearch);
        setData(data);
        checkPayment(data);
      } catch (err) {
        alert(err.message);
      }
    }
  }, [checkPayment, search]);

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

  const downloadPDF = async (values) => {
    setLoading(true);
    const response = await downloadFile();
    setLoading(false);
    window.location.assign(response.pdf);
  };

  const newOrder = async (values) => {
    setSubmitting(true);
    const order = await createOrder();
    const paymentUrl = await initPayment(order);
    setSubmitting(false);
    window.location.assign(paymentUrl);
  };
  const onSubmit = useCallback(
    async (values) => {
      if (paid) {
        try {
          downloadPDF(data);
        } catch (err) {
          console.log(err);
        }
      } else {
        try {
          newOrder(values);
        } catch (err) {
          alert(err.message);
        }
      }
    },
    [data, paid]
  );
  useEffect(() => {
    checkOrder();
  }, [checkOrder]); //checkOrder will change when search changes and effect is called again
  return { onSubmit, submitting, loading };
};


const Form = () => {
  const { onSubmit, submitting, loading } = useCheckPayment(
    window.location.search
  );

  return '';
};

答案 1 :(得分:1)

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

作为依赖项的值将根据被调用的组件而有所不同,这些值可以作为参数传递给挂钩。挂钩也可以返回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>
      }}
    />
}

答案 2 :(得分:0)

基于上面的答案,我想出了以下代码。

挂钩:

const useCheckPayment = ({initialValues, sendUrl, successUrl, description, sum, downloadPDF}) => {
  const [paid, setPaid] = useState(false);
  const [loading, setLoading] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [data, setData] = useState(initialValues);

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

  const checkPayment = useCallback(
    async (search) => {
      try {
        const paid = await getPaymentState(search);
        setPaid(paid);
        document.getElementById('myForm').dispatchEvent(new Event('submit', { cancelable: true }))
      } catch (err) {
        alert(err.message);
      }
    }, []
  );

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

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

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

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

  return { onSubmit, submitting };
};

组件:

const sendUrl = 'https://app.example.com/send'
const successUrl = 'https://example.com/success'
const description = 'Download PDF file'
const sum = '100'

const Form = () => {

  const handleSubmit = (values) => {
    const downloadData = {
      email: values.email,
      phone: values.phone
    }
    onSubmit({ downloadData, values })
  }

  const { onSubmit, submitting } = useCheckPayment(
    {sendUrl, successUrl, description, sum}
  );

  return (
    <Form
      onSubmit={handleSubmit}
      render={({ handleSubmit }) => (
        <form onSubmit={handleSubmit}></form>
      )}
    />
  )
}