问题
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