【问题标题】:Why is my react router calling apis twice?为什么我的反应路由器调用 api 两次?
【发布时间】:2020-12-03 07:22:51
【问题描述】:

我已经探索了整个网络的解决方案,但没有任何对我有用的东西。 我的 Api 被调用了两次,导致数据库中的数据重复。我希望它们只触发一次。

找到下面的代码:

   <Route
        path="/apis/wallet_verification"
        render={(routerProps) => {
          //deals with some params
          console.log("I am triggering");

          axios.get(`http://website/apis/save_ride_payments?payment_type=WALLET`).then((saveRides) =>{
            
            console.log("I am inside get");

            axios.post(`http://website/apis/verify_token?token=${usertoken}`).then((response) => {

              if(response.data.status !== "error") {
                console.log("I am inside post");
                routerProps.history.push({
                  pathname: '/paymentsuccess',
                  state: {initRide}
                });
              }
            })
          })
          console.log("I am out");
          return (
            <div>
              Wallet Transaction
            </div>
          )   
        }}
      />

控制台输出:

I am triggering
I am out
I am inside get
I am triggering
I am out
I am inside post
I am inside get
I am inside post

Network 选项卡显示此 Api 序列:

save_ride_payments
verify_token
save_ride_payments
verify_token

我的 index.js

ReactDOM.render(
<Provider store={store}>
    <App />
</Provider>,
document.getElementById('root'));

当然,可能存在一些逻辑错误。如果有人指出这一点会很高兴。

【问题讨论】:

  • @DrewReese 你能编辑或写一些代码来展示它们应该如何被调用吗?真的很有帮助

标签: reactjs react-redux react-router


【解决方案1】:

您在每次渲染时都进行 Api 调用。你需要一个 useEffect 钩子来只做一次。

任意函数,在这种情况下,您发送给 component 的匿名函数不是 React 函数式组件。要使其成为组件,您需要 1. 命名函数和 2. 此命名函数的首字母为大写 doc

为了让你的代码工作,你需要定义你要渲染的组件,然后你才能调用useEffect。

这是一个简单的例子,缺少一些道具/变量,因为我不确定你是如何以及从哪里得到它们的:

您的新组件:

const Wallet = ({ history }) => {
  useEffect(() => {
    axios
      .get(`http://website/apis/save_ride_payments?payment_type=WALLET`)
      .then((saveRides) => {
        return axios.post(`http://website/apis/verify_token?token=${usertoken}`);
      }).then((response) => {
        if (response.data.status !== 'error') {
          history.push({
            pathname: '/paymentsuccess',
            state: {
              initRide
            },
          });
       }
    });
  }, []);

  return <div>Wallet Transaction</div>;
};

您的路线:

<Route
    path="/apis/wallet_verification"
    component={(routerProps) => <Wallet history={routerProps.history}/>}
/>

如果你使用 react-router,你可以使用 useHistory 钩子来获取历史对象,而不是通过 props 发送。

【讨论】:

  • 我听从了你的建议,但现在两个 Api 都被调用了三次。你能建议其他方法吗?
  • 我需要更多关于你的实现的信息以进一步帮助你,你可以分享更多代码吗?
  • 后端在 Laravel...后端直接调用 'api/wallet_verification' 路由一次...这里是完整 App.js 的要点:gist.github.com/Maha-Waqar/3c1790e08e72014356a49a32bbebecf2
【解决方案2】:

问题

您正在函数体中而不是在useEffect 中进行数据调用、GET 和 POST 请求,因此它们会在任何时候执行 react 渲染组件以用于 DOM 差异化目的。

修复

当路由匹配并呈现匿名组件时,使用useEffect 挂钩运行一次请求。使用一个空的依赖数组,这样效果在挂载时只调用一次。

const WalletVerification = ({ history }) => {
  React.useEffect(() => {
    //deals with some params
    console.log("I am triggering");

    axios
      .get(`http://website/apis/save_ride_payments?payment_type=WALLET`)
      .then((saveRides) => {
        console.log("I am inside get");

        axios
          .post(`http://website/apis/verify_token?token=${usertoken}`)
          .then((response) => {
            if (response.data.status !== "error") {
              console.log("I am inside post");
              routerProps.history.push({
                pathname: "/paymentsuccess",
                state: { initRide }
              });
            }
          });
      });
    console.log("I am out");
  }, []);

  return <div>Wallet Transaction</div>;
}

由于App 已连接到 redux 存储,因此对存储的更新可能会触发重新渲染。如果路由组件使用内联函数,这也会产生重新渲染的效果。

当你使用组件(而不是渲染或子,下面) 路由器使用 React.createElement 从 给定的组件。这意味着如果您向 组件道具,您将在每次渲染时创建一个新组件。这 导致现有组件卸载和新组件 安装而不是仅仅更新现有组件。使用时 用于内联渲染的内联函数,使用 render 或 儿童道具

要么使用Routecomponent 道具

<Route
  path="/apis/wallet_verification"
  component={WalletVerification}
/>

render 道具并代理路由道具

<Route
  path="/apis/wallet_verification"
  render={renderProps => <WalletVerification {...renderProps} />}
/>

此外,您可以使用 React 的 memo 高阶组件进一步提示 React 该组件不应重新渲染。

如果你的组件在给定相同的 props 的情况下呈现相同的结果,你 可以将其封装在对 React.memo 的调用中,以提高某些性能 通过记忆结果的案例。这意味着 React 将跳过 渲染组件,并重用上次渲染的结果。

const WalletVerification = React.memo(({ history }) => { ... });

【讨论】:

  • 这是我在这里遇到的错误:无法在回调中调用 React Hook "React.useEffect"。 React Hooks 必须在 React 函数组件或自定义 React Hook 函数中调用
  • @DandyMandy 嗯,我想 React 没有将 render 函数 as 视为一个反应组件。尝试使用 component 属性。我会更新答案。如果这不起作用,那么我会将其全部分解为一个命名的功能组件并使用component={WalletVerification}(作为示例)。
  • @DandyMandy 认为您可以创建一个 running 代码框来重现我们可以实时调试的这个问题?
  • 我将 UseEffect 代码转移到单独的函数中,然后在路由器中调用它。没有错误,但它再次调用了 api 两次......意味着 useEffect 没有任何好处
  • 你能推荐点别的吗?这么大的代码,无法转入codesandbox
猜你喜欢
  • 2014-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多