4

I created a website using Next.js with React and really like to declare a js variable to define component styles like this:

const foo = {
  fontSize: '12px',
};

And in the render method I do:

render() {
  return (
    <div style={foo}>bar</div>
  );
}

Using ESLint I can quickly find unused styles/variables (if references are gone) and keep the code clean. Hence I do not use <style> or styled-jsx like this:

render() {
  return (
    <div>
      <style>
        .foo {
          font-size: '12px';
        }
      </style>
      <div className="foo">bar</div>
    </div>
  );
}

The problem

The problem with using only js variables instead of <style> is that I cannot apply media queries to target different screen sizes, so I thought I could fix this using javascript:

render() {
  // Bigger font size for smaller devices
  if (windowWidth < 768) foo.fontSize = '24px';

  return (
    <div style={foo}>bar</div>
  );
}

I get windowWidth through this workaround: Get viewport/window height in ReactJS. This works quite well as long as the rendering happens on the client. The problem with this approach is that Next.js will render the view first server-side. At this moment in time, the window/screen size is unknown, hence windowWidth = 0. Thus, I think that the client has to re-render after receiving the initial rendered page from the server.

Question(s):

  1. How can I force the client to immediately re-render after initial render by the server in order to determine the value of windowWidth?

  2. Would this be a good practice/solution or would it lead to performance problems or elements flickering?

Timo Ernst
  • 15,243
  • 23
  • 104
  • 165
  • Did you look into using [`forceUpdate`](https://reactjs.org/docs/react-component.html#forceupdate) into your top component? However, I think you will experience both problems you listed in 2.) depending how you write your styles...if it's mobile-first they will flicker and probably triger layout repaints on desktop and vice-versa for desktop-first. – Teo Dragovic Dec 08 '17 at 14:11
  • You could try hiding responsive elements on page and showing only after update. – Teo Dragovic Dec 08 '17 at 14:17
  • Have you used componentWillMount lifecycle ?? It should work – simbathesailor Dec 08 '17 at 14:19

2 Answers2

2

Found the solution to this. NextJS will automatically re-render client-side after doing server-side render. Adding some console output shows this:

const foo = {
  fontSize: '12px',
};

Render method:

render() {
  if (windowWidth < 768) {
    foo.fontSize = '24px';
    console.log('24px');
  } else {
    console.log('12px');
  }

  return (
    <div style={foo}>bar</div>
  );
}

Viewing on a small screen, this will print:

12px
24px

However, pressing the browser's reload button, the font size doesn't change although the value of the style variable clearly changed.

The solution was to create a copy of the original object and manipulate that instead:

render() {
  let foo2;
  if (windowWidth < 768) {
    foo2 = Object.assign({}, foo, { fontSize: '24px' });
  } else {
    foo2 = Object.assign({}, foo);
  }

  return (
    <div style={foo2}>bar</div>
  );
}

This will change the font size during runtime. However, when the page loads you will see the old 12px font size for the fraction of a second and when client-side render has finished, it will jump to the new font size of 24px. Not sure if I will go with this from a UX perspective but technically this is a working solution. Don't really like it though :-(

Timo Ernst
  • 15,243
  • 23
  • 104
  • 165
0

The best way to trigger a render at any time in reactjs is to create the react element with that property. In your case I would calculate which font size to use first, then create the element with that property.
Font size would not seem to change, but if it did, you’ll probably have to put this state in a common parent element of the elements that use it. This parent would take this state and give it as props to its children.

As a commenter suggests there are ways of triggering an update, but it’s better to reorganize your elements to do this through normal react methods. Don’t be afraid of making more smaller react elements!

Matthew Curry
  • 732
  • 5
  • 15