0

I'm using react-responsive to get media queries and I want to have one component state being shared across screen sizes, but using different wrappers.

Example:

import MediaQuery from 'react-responsive';
import ReactSwipe from 'react-swipe';

const Desktop   = props => <MediaQuery {...props} minWidth={992} />;
const Tablet    = props => <MediaQuery {...props} minWidth={768} maxWidth={991} />;
const Mobile    = props => <MediaQuery {...props} maxWidth={767} />;

export class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <Desktop>
          <SignUpForm />
        </Desktop>
        <Tablet>
          <SignUpForm />
        </Tablet>
        <Mobile>
          <ReactSwipe>
            <SignUpForm />
          </ReactSwipe>
        </Mobile>
      </div>
    );
  }
}

In this example, I want to use another component <ReactSwipe> to encapsulate <SignUpForm />. The above works, but it's creating 3 instances of SignUpForm... if you resize the browser and hit a breakpoint any form data you have filled out already will be lost as the new instance of SignUpForm loads. How do I change this to use media queries but one instance of <SignUpForm />.

Paul Redmond
  • 3,101
  • 1
  • 24
  • 50
  • have you tried react-device-detect from npm? https://www.npmjs.com/package/react-device-detect – Roy Lara Jan 10 '18 at 15:42
  • I haven't, but after a quick look it seems like it does pretty much the same as `react-responsive`? – Paul Redmond Jan 10 '18 at 15:46
  • Excuse the formatting, maybe if instead you make the mediaqueries in the render and use the match conditional from the nom page {(matches) => { if (matches) { return (
    ); } }}
    – Roy Lara Jan 10 '18 at 15:49

1 Answers1

2

Hm. I'm not familiar with MediaQuery, but I'd do this differently. I'd write / find a function that identifies the current platform and then switch based on that:

const wrappers = {
  desktop: Desktop,
  tablet: Tablet,
  mobile: Mobile, // I'd have this wrapper do the ReactSwipe thing
};

export function App() {
  // returns a string that is one of: 'desktop', 'tablet', 'mobile'
  const platform = findPlatform();
  const Wrapper = wrappers[platform];

  return (
    <Wrapper>
      <SignUpForm />
    </Wrapper>
  );
}

Also, as you'll see above, I never use ES6 classes when a function will do. I try to use classes as infrequently as possible. This is personal preference, but I do find that it encourages me to write simpler code.

As asked, here's a possible (untested) implementation of findPlatform. I'd put this in its own module, so it can be mocked more easily during testing.

function findPlatform() {
  const minTabletSize = 768; // Make this whatever you think is best

  if (!(/Mobi/.test(navigator.userAgent))) {
    return 'desktop';
  }

  if (window.outerHeight > minTabletSize || window.outerWidth > minTabletSize) {
    return 'tablet';
  }

  return 'mobile';
}
Christopher Davies
  • 4,161
  • 31
  • 30
  • There are a lot of ways to go about that. From here: https://stackoverflow.com/questions/3514784/what-is-the-best-way-to-detect-a-mobile-device-in-jquery I'd use the `/Mobi/` approach. Pair that with a test of the width/height of the browser to determine if it's tablet vs phone, and you should be good to go. – Christopher Davies Jan 10 '18 at 16:10
  • How would you do the `findPlatform()`? – Paul Redmond Jan 10 '18 at 16:17
  • As mentioned, I'd do what referenced SO question suggests, but as other commenters have suggested, you could also use react-device-detect. I'll update my answer with an (untested) implementation. – Christopher Davies Jan 10 '18 at 16:31
  • Thanks for your updated answer. I think I had double posted my comment. – Paul Redmond Jan 11 '18 at 09:51