【问题标题】:Remount componentDidMount() by path.name通过 path.name 重新挂载 componentDidMount()
【发布时间】:2018-04-18 07:18:04
【问题描述】:

当页面加载时,我在 ReactJSGatsbyJS 项目的布局 index.js 中使用 componentDidMount() 到 document.createElement("script");

componentDidMount () {
  const tripadvisorLeft = document.createElement("script");
  tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
  tripadvisorLeft.async = true;
  document.body.appendChild(tripadvisorLeft);
}

然后这会请求显示数据并且工作正常。但是,当我 <link to=... 另一个页面使用 gatsby-link(想象同样的问题适用于 react-router)时,componentDidMount() 已经运行,因此它不会再次获取数据。

如何确保在每次path 更改后此脚本为mounted,或者通过特定的path 更好?

【问题讨论】:

  • 只是为了确定,您是否希望componentDidMount 的主体(即获取脚本)在每次路由更改时发生?
  • React Router 提供了像 onChangeonUpdate 这样的钩子,你可以用它们来做一些你想要实现的事情。 See this related stack overflow question
  • 理想情况下只在某些路线上,例如root/reviews/,但在这个阶段一切都将是一个很好的起点。也不是componentDidMount 的全部功能只有`const tripadvisorLeft`
  • 谢谢@ChiragRavindra。我只是在寻找 gatsby-link
  • 您使用的是哪个版本的react-router

标签: javascript reactjs react-router gatsby


【解决方案1】:

您可以使用gatsby-link 包中的navigateTo 来滚动您自己的Link 组件,该组件在导航之前执行您的自定义逻辑。

Link.jsx

import React from "react";
import { navigateTo } from "gatsby-link";

const RESOURCE_PATH = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
const refetchAndNavigate = (path) => () => {
  // refetch
  const tripadvisorLeft = document.createElement("script");
  tripadvisorLeft.src = RESOURCE_PATH;
  tripadvisorLeft.async = true;
  document.body.appendChild(tripadvisorLeft);

  // finally navigate
  navigateTo(path);
}

const Link = ({ to, ...propsToPass }) => (
  <div {...propsToPass} onClick={refetchAndNavigate(to)}/>
);

export default Link;

我没有测试过这段代码,但想法会起作用。

【讨论】:

  • 感谢您的提问。我玩过你的想法,认为它正朝着我需要的方向发展,但有些问题阻碍了我。这将在整个站点中重新安装脚本?我有多个脚本需要根据它们的组件进行安装,其中一些通过 GraphQl 使用数据来构建 URL。这在您的示例中是不可能的。想知道我们是否可以开始实时聊天并分享源链接?
  • @Darren 听起来您需要访问传递到组件的道具才能首先构建 API。如果是这种情况,您需要在componentDidUpdate 中执行您的获取逻辑。另外,你看过React portals吗?
  • 感谢您的回复。是的,我虽然关于门户网站,但这无济于事。在寻找解决方案时,我从未如此沮丧过。如果您有时间查看源代码github.com/discovr-bookings/mountainWhispers 是否可以帮助您了解我如何实施您的建议。您还可以在此处查看显示问题 gracious-allen-d5eb54.netlify.com/varenna/reviews 的已部署版本。干杯。
【解决方案2】:

componentDidMount 第一次运行(也是第一次!)组件被挂载在 DOM 中。

componentDidUpdate 每次组件接收到新的 props 时都会运行,但不会在初始渲染时运行。

为了在 DOM 上操作第一次渲染和后续更新,您需要在 componentDidMount 中注册您的 &lt;script&gt; 处理逻辑> componentDidUpdate,即

class YourComponent extends React.Component {
  componentDidMount () {
    const tripadvisorLeft = document.createElement("script");
    tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
    tripadvisorLeft.async = true;
    document.body.appendChild(tripadvisorLeft);

    // Keep track of the script tag
    this.scriptTag = tripadvisorLeft
  }

  componentDidUpdate (prevProps) {
    // Figure out if the path changed
    if (this.props.path !== prevProps.path) {
      // Remove the old script tag
      document.body.removeChild(this.scriptTag)

      // Add it back
      const tripadvisorLeft = document.createElement("script");
      tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
      tripadvisorLeft.async = true;
      document.body.appendChild(tripadvisorLeft);

      this.scriptTag = tripadvisorLeft
    } 
  }
}

您可以重构它以将 &lt;script&gt; 创建逻辑移动到辅助函数中,但我已将其内联到此处以明确发生了什么。

【讨论】:

  • 谢谢@bgran。我喜欢脚本被删除和替换。它适用于我提出的问题。
  • 我唯一的查询是我有多个使用此组件的帖子,但每个帖子都替换了 url 查询中的 locationID。因此,当在每个帖子中使用此脚本时,它不会用新的locationID 替换脚本,除非Link to= 路径然后退出并再次返回。这是否意味着 if (this.props.path !== prevProps.path) { 未被识别?
  • 如果没有看到您的代码,很难确切地说出发生了什么 - 如果您可以使用不起作用的代码创建一个新分支,我可以提供一些更具体的反馈。您应该比较的可能不是 path 道具。它可能被称为pathname。如果您在componentDidUpdate 中设置断点,您可以在单击&lt;Link&gt; 时比较道具并查看具体发生了什么变化。
  • 谢谢@bgran。我在这里stackoverflow.com/questions/50206298/… 发布了一个与此额外麻烦有关的问题。在这里你可以看到完整的代码。再次感谢您。
【解决方案3】:

您可以添加在组件更新时调用的componentDidUpdate生命周期方法:

componentDidUpdate(prevProps) {
  // check if path has changed
  if (prevProps.pathname !== this.props.pathname) { 
    // call something to fetch data
  }
}

您可以添加更多条件以仅匹配某些路径。

检查您的 gastby-link 库,了解如何获取当前路径名以作为道具传递。

您还可以尝试从父组件传递pathname={window.location.pathname},当路径更改时,该组件会重新渲染。

【讨论】:

  • 谢谢。没有为我工作对不起。通过componentDidUpdate 加载script 时,我没有安装脚本,添加或不添加条件。有什么想法吗?
  • 您可以尝试删除然后重新插入tripadvisorLeft 元素到componentDidUpdate 中吗?也许在脚本运行后尝试this.forceUpdate()(确保添加路径更改检查,否则它将进入无限递归渲染)。还要检查componentDidUpdate 是否确实被调用了。
  • 感谢您的回复。我已经尝试了很多方法,但无法开始工作 - 现在回到使用 componentDidMount。这是来源github.com/discovr-bookings/mountainWhispers/blob/master/src/…,如果这可以帮助您了解我如何实施您的建议。您还可以在此处查看显示问题的已部署版本gracious-allen-d5eb54.netlify.com/varenna/reviews
  • 您是否尝试过我上面的建议(重新插入脚本元素)? componentDidUpdate 被叫到了吗?也可以试试tripadvisorLeft.onload = () =&gt; this.forceUpdate()。添加日志以检查是否正在调用 this.forceUpdate() 等。
  • 是的,试过了,但是脚本没有被调用。将尝试forceUpdate,但肯定已经尝试过了。
【解决方案4】:

您能否使用 HOC 来完成您想要的。我只是把它放在一起,它需要修改。

创建一个类似的组件,

const withTracking = (WrappedComponent) => {
    return class withTracking extends React.Component {
        constructor(props) {
            super(props);

            this.trackit = this.trackIt.bind(this);
        }

        trackIt() {
            {
                const tripadvisorLeft = document.createElement("script");
                tripadvisorLeft.src = "https://www.jscache.com/wejs?wtype=selfserveprop&uniq=789&locationId=10467767&lang=en_NZ&rating=true&nreviews=0&writereviewlink=true&popIdx=true&iswide=true&border=false&display_version=2";
                tripadvisorLeft.async = true;
                document.body.appendChild(tripadvisorLeft);
            }
        }

        render() {
            return (
                <div>
                    <WrappedComponent {...this.props} trackIt={this.trackIt} />
                </div>
            );
        }
    }
};

然后在 react router 中像这样包装它,

<Route exact path="/pet-of-the-week" component={withTracking(TEST)}/>

并且在组件中使用componentDidMount,

const TEST = (WrappedComponent) => {
    return class ClickLogger extends React.Component {
        constructor(props) {
            super(props);
        }

        componentDidMount() {
            this.trackIt();
        }

        render() {
            return (
                <div>
                    <h1>something</h1>
                </div>
            );
        }
    }
};

【讨论】:

    猜你喜欢
    • 2019-08-16
    • 2020-07-08
    • 2021-05-03
    • 1970-01-01
    • 2019-03-25
    • 2022-08-03
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多