Remount componentDidMount() by path.name

别来无恙 提交于 2019-12-02 18:56:28

问题


When the page loads I use componentDidMount() to document.createElement("script"); in the layout index.js of a ReactJS and GatsbyJS project as

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);
}

This then requests the data to be displayed and works fine. However, when I <link to=... another page using gatsby-link (imagine the same issue applies for react-router), componentDidMount() has already run so it won't fetch the data again.

How can I ensure that this script is mounted after each path change, or better by a specific path?


回答1:


componentDidMount runs the first time (and only the first time!) the component is mounted in the DOM.

componentDidUpdate runs every time the component receives new props, but not for the initial render.

In order to operate on the DOM for the first render and for subsequent updates, you'll need to register your <script> handling logic in both componentDidMount and componentDidUpdate, i.e.

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
    } 
  }
}

You can refactor this to move the <script> creation logic into a helper function, but I've inlined it here to make it clear what's going on.




回答2:


You can use navigateTo from the gatsby-link package to roll your own Link component that executes your custom logic before navigating.

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;

I have not tested this code but idea will work.




回答3:


You can add in the componentDidUpdate lifecycle method which is called whenever the component is updated:

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

You can add more conditions to match only certain paths.

Check your gastby-link library to see how you can get the current pathname to pass in as props.

You can also try passing pathname={window.location.pathname} from a parent component that gets re-rendered when path changes.




回答4:


Could you use a HOC to accomplish what you want. I just threw this together, it will need to be modified.

Create a component something like,

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>
            );
        }
    }
};

Then in react router wrap it like,

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

And in the component use componentDidMount,

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

        componentDidMount() {
            this.trackIt();
        }

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


来源:https://stackoverflow.com/questions/49893481/remount-componentdidmount-by-path-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!