问题
In production mode, a URL parameter value can not be used to render dynamic attribute values. The same URL parameter value can be used to render a specific component.
I have set up a repo with a minimum reproducible example.
https://github.com/mikepuglisi/gatsby-dynamic-attribute-bug
We've been able to work around this by storing the parameter values in state, but I don't see why that should be necessary (especially since it renders the correct div).
Relevant Code (src/pages/index.js)
const IndexPage = ({location}) => {
const params = new URLSearchParams(location.search);
const color = params.get('color');
return (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
{ color ?
<p style={{color: color}}>
I SHOULD BE THE COLOR {color} in production mode even after hitting CTRL+F5 (hard refresh)
</p> :
<p>
No Color was passed. Add ?color=blue to URL and hit CTRL+F5 to ensure a hard refresh
</p>
}
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
<Image />
</div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
}
The url parameter ?color=blue
should be able to render the appropriate div AND render the appropriate style. The div shows correctly, but the correct style does not.Issue Screenshot
回答1:
This problem seems to be how the hydration
process works for server rendered React applications.
Short Answer
You need to set an initial state for the color and then update it in a useEffect
hook:
const IndexPage = ({location}) => {
const [color, setColor] = useState();
useEffect(() => {
setColor(new URLSearchParams(location.search).get('color'));
}, location)
return (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
{ color ?
<p style={{color: color}}>
I SHOULD BE THE COLOR {color} in production mode even after hitting CTRL+F5 (hard referesh)
</p> :
<p>
No Color was passed. Add ?color=blue to URL and hit CTRL+F5 to ensure a hard refresh
</p>
}
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
<Image />
</div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
}
Long Answer
When building your Gatsby app for production, it renders out static HTML for you using ReactDOMSever. When you first load a page in the browser, it firsts loads the static HTML and then it needs to boot up React and hydrate your app.
Upon hydrating your app, React will expect your HTML content to be identical to what would have been rendered on the first render of client-side rendered React application. However, in your case, this is not true.
On the first render for your React component, you should have a style
attribute on your paragraph element with the value of the color found in the URL query params. When building the static HTML for the page, you will not have the style
attribute on your paragraph element because query params don't exist on the server.
You might be wondering why the text content correctly renders the color value from your query params on the initial render. This is because the hydrate method can patch differences in text content, but it cannot patch differences in your HTML attributes, which is what is happening with your style
attribute on the paragraph element.
From the React docs for the hydrate
method:
React expects that the rendered content is identical between the server and the client. It can patch up differences in text content, but you should treat mismatches as bugs and fix them. In development mode, React warns about mismatches during hydration. There are no guarantees that attribute differences will be patched up in case of mismatches. This is important for performance reasons because in most apps, mismatches are rare, and so validating all markup would be prohibitively expensive.
Because of this "mismatch" between the initial HTML content, you should instead set a default value for color
as a state property, and then update that value when the component first mounts. That way, when you first load the app on the client and you have a color
query param present, you will be able to update the state on mount to trigger a re-render of your React component.
Resources
- Gatsby: Building the JavaScript App
- ReactDOM hydrate() method
来源:https://stackoverflow.com/questions/56398811/unable-to-update-jsx-attribute-based-on-url-parameter-in-a-gatsby-app-running-in