Unable to update JSX attribute based on URL parameter in a Gatsby app running in production

烈酒焚心 提交于 2020-01-13 18:54:52

问题


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

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