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

@cross2d/next-redux-wrapper

v1.3.3

Published

Redux wrapper for Next.js

Downloads

5

Readme

Redux wrapper for Next.js

Build status

Usage

npm install next-redux-wrapper --save

Wrapper has to be attached your page components (located in /pages). For safety it is recommended to wrap all pages, no matter if they use Redux or not, so that you should not care about it anymore in all child components.

Here is the minimal setup (makeStore and reducer usually are located in other files):

import React, {Component} from "react";
import {createStore} from "redux";
import withRedux from "next-redux-wrapper";

const reducer = (state = {foo: ''}, action) => {
    switch (action.type) {
        case 'FOO':
            return {...state, foo: action.payload};
        default:
            return state
    }
};

/**
* @param {object} initialState
* @param {boolean} options.isServer indicates whether it is a server side or client side
* @param {Request} options.req NodeJS Request object (if any)
* @param {boolean} options.debug User-defined debug mode param
* @param {string} options.storeKey This key will be used to preserve store in global namespace for safe HMR 
*/
const makeStore = (initialState, options) => {
    return createStore(reducer, initialState);
};

class Page extends Component {
    static getInitialProps({store, isServer, pathname, query}) {
        store.dispatch({type: 'FOO', payload: 'foo'}); // component will be able to read from store's state when rendered
        return {custom: 'custom'}; // you can pass some custom props to component from here
    }
    render() {
        return (
            <div>
                <div>Prop from Redux {this.props.foo}</div>
                <div>Prop from getInitialProps {this.props.custom}</div>
            </div>
        )
    }
}

Page = withRedux(makeStore, (state) => ({foo: state.foo}))(Page);

export default Page;

How it works

No magic is involved, it auto-creates Redux store when getInitialProps is called by Next.js and then passes this store down to React Redux's Provider, which is used to wrap the original component, also automatically. On the client side it also takes care of using same store every time, whereas on server new store is created for each request.

The withRedux function accepts makeStore as first argument, all other arguments are internally passed to React Redux's connect() function for simplicity. The makeStore function will receive initial state as one argument and should return a new instance of redux store each time when called, no memoization needed here, it is automatically done inside the wrapper.

withRedux also optionally accepts an object. In this case only 1 parameter is passed which can contain the following configuration properties:

  • createStore (required, function) : the makerStore function as described above
  • storeKey (optional, string) : the key used on window to persist the store on the client
  • debug (optional, boolean) : enable debug logging
  • mapStateToProps, mapDispatchToProps, mergeProps (optional, functions) : functions to pass to react-redux connect method
  • connectOptions (optional, object) : configuration to pass to react-redux connect method

When makeStore is invoked it is also provided a configuration object as the second parameter, which includes:

  • isServer (boolean): true if called while on the server rather than the client
  • req (Request): The next.js getInitialProps context req parameter
  • query (object): The next.js getInitialProps context query parameter

The object also includes all configuration as passed to withRedux if called with an object of configuration properties.

Use withRedux to wrap only top level pages! All other components should keep using regular connect function of React Redux.

Although it is possible to create server or client specific logic in both createStore function and getInitialProps method I highly don't recommend to have different behavior. This may cause errors and checksum mismatches which in turn will ruin the whole purpose of server rendering.

I don't recommend to use withRedux in both top level pages and _document.js files, Next.JS does not have provide a reliable way to determine the sequence when components will be rendered. So per Next.JS recommendation it is better to have just data-agnostic things in _document and wrap top level pages with another HOC that will use withRedux.

Async actions in getInitialProps

function someAsyncAction() {
    return {
        type: 'FOO',
        payload: new Promise((res) => { res('foo'); })
    }
}

function getInitialProps({store, isServer, pathname, query}) {
    
    // lets create an action using creator
    const action = someAsyncAction();
    
    // now the action has to be dispatched
    store.dispatch(action);
    
    // once the payload is available we can resume and render the app
    return action.payload.then((payload) => {
        // you can do something with payload now
        return {custom: 'custom'}; 
    });
    
}

Usage with Immutable.JS

If you want to use Immutable.JS then you have to modify your makeStore function, it should detect if object is an instance of Immutable.JS, and if not - convert it using Immutable.fromJS:

export default function makeStore(initialState = {}) {
    // Nasty duck typing, you should find a better way to detect
    if (!!initialState.toJS) initialState = Immutable.fromJS(initialState);
    return createStore(reducer, initialState, applyMiddleware(thunk));
}

The reason is that initialState is transferred over the network from server to client as a plain object (it is automatically serialized on server) so it should be converted back to Immutable.JS on client side.

Here you can find better ways to detect if an object is Immutable.JS: https://stackoverflow.com/a/31919454/5125659.

Resources