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 🙏

© 2026 – Pkg Stats / Ryan Hefner

redux-async-load

v0.2.4

Published

This module merges the logic of loading data on the client side and the server side.

Readme

redux-async-load

This module merges the logic of loading data on the client side and the server side.

Purpose

With react, we use to do async things (generally loading data etc...) in component life cycles. When using redux with react, your connected components will generally be instances of "PureComponent", that means they are stateless. (read more about react pure component and why this concept was introduced) This difference is a big thing, With redux, you store your data into the state (that is connected to props), not into component state.

When using SSR (Server side rendering), components do not have life cycles, the "render" only occurs once (renderToString). To have the state totally hydrated, we need to render multiple times. During each render, components might dispatch actions to the store and the redux state might change, changing the result of the next render.

On client side, this is not a problem, when the state changes, connected components receives new props and will update.

So we need a module that can re-render until the state is ready without implementing a specific server logic. This module use the "componentWillMount" event on both env to dispatch loading actions, and trigger events to mark async components as loaded in the state. On server side, each time the store receives a event like this, he will re render, until all the components are ready, then send the response to browser.

SSR workflow example:
  1. Server is rendering
  2. Component "Foo" dispatch an action to tell he is loading, and start doing jobs, result is going to the state to be connected later.
  3. Data are loaded, "Foo" dispatch an action to tell he is ready
  4. Store receive this action and examine the list of remaining actions
    • The list of remaining actions appears to be empty, that mean the state should have all required data to render "foo", do a final render.
    • OR
    • The list of remaining actions is not empty, "foo" or an other component is loading => we wait for next state notification then re render => (Go to 1.)

This allow us to have a deep three rendering on server side.

Use case

With the new react router v4, routes are loaded in components, if you have some routes inside a component that is deep in your three, react won't see this route during the renderToString. react-async-load is a tool to build you state data using re-render on server side.

Install

npm install redux-async-load --save

Api

renderAsync(store: Object, render: () => string, stateKey: string = 'asyncLoad'):Promise<string>

For Server side only

Args
  • store: Object
    • the redux store implementing the reducer from this module and the rest of your data
  • render: Function
    • no args
    • return string
    • This method will be invoked for each render on server side, you must return the html of your app using renderToString from react-dom
  • stateKey: string
    • default: 'asyncLoad'
    • This is the key of the reducer in your state
Returns

A Promise with the final html string of the application

stateSelector:(key = 'asyncLoad', state: {Object}) => asyncState: {[loadId]: {loading: boolean}}

stateSelector is used to find the asyncState inside the redux state.

  • key: string
    • The key of the asyncState in the redux state (default = 'asyncLoad')
  • state: Object
    • The redux state

isReady:(asyncState: {[loadId]: {loading: boolean}}) => boolean

isReady will check the asyncState to know if all components are loaded

  • asyncState: Object
    • This is the state of async components. Use the stateSelector to get it from the redux state
  • return boolean true if all components are loaded

Action creator

asyncSetStatus:(loadId, {loading: boolean})

  • loadId: string
    • This is unique identifier of the operation in the asyncState
  • return an action

Components

<ReduxLoader />

props

  • loadId: string The Id of the connected element
  • shouldLoad:(props) => boolean
    • props: Object The props passed to the component
    • If this method return true, and if the component is not marked as loaded in state, the component will call the load function passed in props
  • load: (props) => Promise<any> | null
    • props Object The props passed to the component
    • If this method returns a promise once load has been done, the loaded status of the component will be set automatically to false. If not, you will have to dispatch the action asyncSetStatus Normally, It should returns the result of an action
  • shouldReload: (newProps, oldProps) => boolean
    • newProps: Object The new props of the component
    • oldProps: Object The old props of the component
    • If this method return true, the component will call the load function passed in props
  • render: (props) => React.Element If this property is present, It'll be used as the default render, and must return a single valid component.
    • props: Object The props passed to the component

Usage

Add the reducer to your redux store (store.js)

import {createStore, combineReducers}  from 'redux'
import {reducer as asyncLoad} from 'redux-async-load'

export default createStore(combineReducers({
   asyncLoad, // <-- add the reducer to your reducers 
   //[your reducers]
}), yourInitialState)

Create an async component

import React, {Component} from 'react'
import {connect} from 'react-redux'
import {ReduxLoader} from 'redux-async-load'

//this is a redux action creator to load data that will use index to load the user in state.
import {myLoadAction} from './action'

const User = props => <p>{props.user && props.user.name}</p>

/*
 * Props that represent loading logic are implemented in the ReduxLoader
 * If your props are dynamics and represent data or identifiers, you should implement them directly on the AsyncUser
 * Model: AsyncUser(props) => ReduxLoader({...logicProps, props})
 */
const AsyncUser = props => <ReduxLoader 
    {/* Do load only if we do not have data */}
    shouldLoad={props => !props.data}
    
    {/* This tells the component how to load your data */}
    {/* This is not invoked if you pass an action creator named "load" to connect because it will replace this props */}
    load={props => myLoadAction(props.userId)}
    
    {/* The component receives props, tell him if he must reload data when userId change */}
    shouldReload={(props, oldProps) => (props.userId !== oldProps.userId)}
    
    {/* This is the render method, use it to an altenrative way to render children */}
    {/* If the component contains chilren, they will be present in props.children */}
    render={props => <User {...props} />}
>
    {/* OR */}
    {/* This is the normal way of rendering an element */}
    {/* <User /> will be cloned with all props */}
    {/* If the prop render is present, children will be passed as props */}
    <User />
</ReduxLoader>


//connect our component to the state
export default connect((state, {userId}) => {
    return {
        //loadId is required by Redux loader to set the loading flag in redux async state
        loadId: userId
        //We suppose your load action and his reducer will hydrate this part of the state with data (used buy AsyncUser)
        data: state.user[userId]
    }
}, ({load}))(AsyncUser)

Use the new async component where you want (app.js)

export default props => (
    <div>
        <p>This component was preloaded on server, see the source code...</p>
        <AsyncUser userId={12345}/>
    </div>
)

Render on server side (server.js)

import {renderToString} from 'react-dom'
import {renderAsync} from 'redux-async-load'
import {Provider} from 'react-redux'
import store from './store'
import App from './app'
import Html from './html' // <= your html template
import express from 'express'

const app = new express()

app.use((req, res, next) => {
    
    //Your other jobs
    
    //render async will subscribe to the store, and knows when the data are loaded
    renderAsync(store, () => renderToString(<Provider store={store}><App /></Provider>))
        //Render the generated html as you do normally
        .then(appHtml => {
            // Build your html structure with your favorite tool (like Helmet)
            const html = <Html content={appHtml} /> 
            res.send('<!doctype html>\n' + html)
        })
        .catch(next)  
})

Render on client side as usual (client.js)

import {render} from 'react-dom'
import {Provider} from 'react-redux'
import store from './store'
import App from './app'

render(<Provider store={store}><App /></Provider>, document.getElementById('app-root'))

Todo

  • add examples
  • add tests

Disclamer

This is clearly experimental, and maybe removed if a better solution is found. You should take all precautions to prevent huge loading. If the schema of your app is very deep, you might want to preload data in a high order component that will prevent re rendering one more time