npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-responsive-redux

v0.10.1

Published

redux integration for react-responsive

Downloads

1,279

Readme

react-responsive-redux npm version

The Problem

If you use react-responsive and server-side-rendering you've probably come across this cryptic warning in your browser console before:

react 16

Warning: Expected server HTML to contain a matching in .

react 15

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server: (client) actid="1">Foo</h1><div data-react (server) actid="1">Bar</h1><div

This happens when the server and client disagree about the state of the DOM. With react-responsive specifically it can be caused by code like this:

<MediaQuery maxWidth={991}>
  <div>You are a mobile device</div>
</MediaQuery>
<MediaQuery minWidth={992}>
  <div>You are a desktop</div>
</MediaQuery>

On the client side this works just fine, but on the server side it doesn't because there's no DOM to query, so both components render empty on the server. Once react takes over on the client side it will re-render these elements correctly based on the provided media queries.

react-responsive offers the values prop as a workaround:

<MediaQuery maxWidth={991} values={{ width: 1024 }}>
  <div>You are a mobile device</div>
</MediaQuery>  
<MediaQuery minWidth={992} values={{ width: 1024 }}>
  <div>You are a desktop</div>
</MediaQuery>

This example will work fine as long as your browser window is >= 1024px. If it's not you're back to the warning above.

A Solution

react-responsive-redux combines several pieces together to work around this issue.

  1. The User-Agent string is sniffed to get a reasonable guess of the client's screen size
  2. This value is stored in redux store so it's globally accessible
  3. Wrapped versions of MediaQuery are provided which get their width from the global store.

The end result is that it's possible to do server-side rendering correctly for responsive pages which change for mobile, tablet, or desktop users (it also means that react warning probably disappears):

<MobileScreen>
  <div>You are a mobile device</div>
</MobileScreen>
<DesktopScreen>
  <div>You are a desktop</div>
</DesktopScreen>

Installation

Install with npm:

npm install react-responsive-redux

react-responsive-redux has the following peerDependencies:

"prop-types": "^15.5.10",
"react": "^15.6.1 || ^16.0.0",
"react-redux": "^5.0.6",
"redux": "^3.7.2"

Usage

Example

Inside the example directory run:

$ yarn install
$ yarn build
$ yarn start

Try loading the page with chrome on a desktop display and pretending to be a mobile device. In both cases you'll notice the initial redux state is correct and there's no error in the console.

Overview

To use react-responsive-redux you need to do the following:

  1. Add a redux reducer to your store
  2. Add mobile detection and an action dispatch in your request handler
  3. Use a wrapped component

redux setup

Add the responsive reducer into your redux store:

ES5 Example

var redux = require('redux')
var responsiveReducer = require('react-responsive-redux').reducer

var reducers = {
  // ... your other reducers here ...
  responsive: responsiveReducer
}
var reducer = redux.combineReducers(reducers)
var store = redux.createStore(reducer)

ES6 Example

import { createStore, combineReducers } from 'redux'
import { reducer as responsiveReducer } from 'react-responsive-redux'

const reducers = {
  // ... your other reducers here ...
  responsive: responsiveReducer
}
const reducer = combineReducers(reducers)
const store = createStore(reducer)

server setup

Add mobile detection and dispatch a redux action during your request handler. This must happen after you've created your redux store and before you do your server-side rendering.

(Note: these examples are based on the redux SSR example and are incomplete as-is).

ES5 Example

var setMobileDetect = require('react-responsive-redux').setMobileDetect
var mobileParser = require('react-responsive-redux').mobileParser
var reducers = require('./reducers')
var renderToString = require('react-dom/server').renderToString

function handleRender(req, res) {
  // Create a new Redux store instance
  var store = createStore(reducers)
  var dispatch = store.dispatch

  // do our mobile detection
  var mobileDetect = mobileParser(req)

  // set mobile detection for our responsive store
  dispatch(setMobileDetect(mobileDetect))

  // Render the component to a string
  var html = renderToString(
    <Provider store={store}>
      <App />
    </Provider>
  )

  // Grab the initial state from our Redux store
  var preloadedState = store.getState()

  // Send the rendered page back to the client
  res.send(renderFullPage(html, preloadedState))
}

ES6 Example

import { setMobileDetect, mobileParser } from 'react-responsive-redux'
import reducers from './reducers'
import { renderToString } from 'react-dom/server'

function handleRender(req, res) {
  // Create a new Redux store instance
  const store = createStore(reducers)
  const { dispatch } = store

  // do our mobile detection
  const mobileDetect = mobileParser(req)

  // set mobile detection for our responsive store
  dispatch(setMobileDetect(mobileDetect))

  // Render the component to a string
  const html = renderToString(
    <Provider store={store}>
      <App />
    </Provider>
  )

  // Grab the initial state from our Redux store
  const preloadedState = store.getState()

  // Send the rendered page back to the client
  res.send(renderFullPage(html, preloadedState))
}

React Components

responsiveWrapper(props={})

responsiveWrapper is a wrapper to generate redux-connected MediaQuery components. These components have width and deviceWidth set in the values prop from the connected store. All props are passed through to the underlying <MediaQuery instance.

The following convenience components are exported and are all are built using responsiveWrapper:

Bootstrap sizes

<XsScreen>

export const XsScreen = responsiveWrapper({ maxWidth: 767 })

<XsScreenHidden>

export const XsScreenHidden = responsiveWrapper({ minWidth: 768 })

<SmScreen>

export const SmScreen = responsiveWrapper({ query: `(min-width: 768px) and (max-width: 992px)` })

<SmScreenHidden>

export const SmScreenHidden = responsiveWrapper({ query: `(max-width: 767px), (min-width: 993px)` })

<MdScreen>

export const MdScreen = responsiveWrapper({ query: `(min-width: 993px) and (max-width: 1199px)` })

<MdScreenHidden>

export const MdScreenHidden = responsiveWrapper({ query: `(max-width: 992px), (min-width: 1200px)` })

<LgScreen>

export const LgScreen = responsiveWrapper({ minWidth: 1200 })

<LgScreenHidden>

export const LgScreenHidden = responsiveWrapper({ maxWidth: 1199 })
Generic

<PhoneScreen>

alias for <XsScreen>

<PhoneScreenHidden>

alias for <XsScreenHidden>

<TabletScreen>

alias for <SmScreen>

<TabletScreenHidden>

alias for <SmScreenHidden>

<MobileScreen>

export const MobileScreen = responsiveWrapper({ maxWidth: 991 })

<MobileScreenHidden>

alias for <DesktopScreen>

<DesktopScreen>

export const DesktopScreeen = responsiveWrapper({ minWidth: 992 })

<DesktopScreenHidden>

alias for <MobileScreen>

ES5 Example

var React = require('react')
var MobileScreen = require('react-responsive-redux').MobileScreen
var DesktopScreen = require('react-responsive-redux').DesktopScreen

function Component() {
  return(
    <div>
      <MobileScreen>
        <div>You are a mobile device</div>
      </MobileScreen>
      <DesktopScreen>
        <div>You are a desktop</div>
      </DesktopScreen>
    </div>    
  )
}

ES6 Example

import React from 'react'
import { MobileScreen, DesktopScreen } from 'react-responsive-redux'

const Component = () =>
  <div>
    <MobileScreen>
      <div>You are a mobile device</div>
    </MobileScreen>
    <DesktopScreen>
      <div>You are a desktop</div>
    </DesktopScreen>
  </div>

Breakpoints

The current breakpoints are based on bootstrap's sizings.

|Device Type| Breakpoint | |-----------|-----| | xs | max-width: 767px | | sm | min-width: 768px, max-width: 991px | | md | min-width: 992px, max-width: 1199px | | lg | min-width: 1200px | | Phone | max-width: 767px | | Tablet | min-width: 768px, max-width: 991px | | Mobile | max-width: 991px | | Desktop | min-width: 992px |

Fake Widths

Device detection is done using mobile-detect which allows us to get the following information:

  • mobile - is the device mobile (ie, phone or tablet)
  • phone - is the device a phone?
  • tablet - is the device a tablet?
  • desktop - the opposite of mobile

Based on this information and our breakpoints we set a fake screen size in our store:

|Device Type| Fake Width (in px)| |-----------|-----| | Phone | 767 | | Tablet | 991 | | Mobile | 767 | | Desktop | 1200 |

Note that the desktop fake width is set to 1200px rather than 992px - this is because worldwide trends indicate that most desktop views are >= 1200px and defaulting the desktop size to this means that <LgScreen> / <LgScreenHidden> are more likely to render correctly on the server side.

Examples for rendering to different components

import React from 'react'
import { MobileScreen, DesktopScreen } from 'react-responsive-redux'

const Component = () =>
  <div>
    <MobileScreen component="p">
      You are a mobile device
    </MobileScreen>
    <DesktopScreen component={React.Fragment}>
      You are a desktop
    </DesktopScreen>
  </div>

TODO

  • Add support for custom breakpoints
  • Add support for custom screen sizes