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

redux-async-props

v0.2.1

Published

Load props and state asynchonously with react-router and redux

Downloads

75

Readme

redux-async-props

A simple way to load props and state asynchonously with react-router and redux.

Why?

Often pages will have a bunch of resources they need to fetch before rendering properly. The easy way to do this is add all your fetching logic to the componentDidMount method. However this offers a poor user experience as the user has to wait for the page to load and then wait again for the data to be fetched. The best way is to have everything that's needed already loaded in the initial redux state and passed in as props on the first render.

How to use

Define what each component needs with a needs property. This should be a function that accepts the router props and then the redux store object. e.g.

MyComponent.needs = (props, store) => 
  store.dispatch(doMyAsyncAction())
  .then(() => {
    var state = store.getState()
    return {
      myData: state.myAsyncData,
    }
})

Why do we pass the store in and not the state? This allows more freedom, you can construct the promise in any way shape or form that makes sense for optimal data loading. If you want to return new props to the component just be sure to return an object at the end of the promise chain.

Any redux actions you dispatch here will also populate your redux store, which means you could use the react-redux connect decorator to map your redux state to props on the first render if that's your preferred workflow.

You'll need to configure your server and client. See the next steps below, the steps assume you're familiar with how to setup redux and react-router, if you're new to these two packages it might be best to start with their tutorials first.

Configuring the server

import { fetchNeeds, AsyncRouterContext } from 'redux-async-props'
// ...
app.get('*', function (req, res, next) {
  match({ routes: routes, location: req.url }, (err, redirect, props) => {
  const store = createStore(reducer, applyMiddleware(callAPIMiddleware))
    fetchNeeds(props, store)
    // This param contains the fetched async props for each route
    .then((asyncProps) => {
      const appHtml = renderToString(
          // Ensure you wrap inside a redux Provider as the AsyncRouterContext 
          // depends on having the redux store in the context.
        <Provider store={store}>
            {/* We need to use the AsyncRouterContext so it can handle using
              * the async props on the initial render. */}
          <AsyncRouterContext {...props} asyncProps={asyncProps} />
      </Provider>
      )
      // dump the HTML into a template, lots of ways to do this.
      var html = require("raw!./public/index.html")
      html = html.replace('<!--__APP_HTML__-->', appHtml)
      // Our initialState is slightly different to the norm, instead of being 
      // made up of only the redux store, it now also contains the async props 
      // for the initial render (we'll use these on the client to avoid 
      // rendering twice).
      const initialState = {asyncProps, store: store.getState()}
      html = html.replace('{/*__INITIAL_STATE__*/}', JSON.stringify(initialState))
      res.send(html)
    })
  })
})

Configuring the client

import { AsyncRouterContext } from 'redux-async-props'
//...
const initialState = window.__INITIAL_STATE__
const store = createStore(reducer, initialState.store)
render((
  <Provider store={store}>
    <Router 
      routes={routes} 
      history={browserHistory}
      render={(props) => <AsyncRouterContext 
        {...props} 
        // Pass in the async props that we're hydrating from
        // the server, these are needed so that the initial render
        // only needs to be done once.
        asyncProps={initialState.asyncProps}
      />}
    />
  </Provider>
), document.getElementById('app'))

The client is rendered synchonously so we can't wait for any data to be fetched. This is why we store the initial async props that are generated on the server and then hydrate them for the first render.

Navigating to new pages will execute each new component's needs. You can minimise the number of requests sent by checking if your redux state already contains the data needed. If so don't go and fetch it from the server again.

Things that have to work

Your asynchronous actions have to run on both the client and server. If your using fetch then the isomorphic-fetch is a pretty good package for ensuring it works on the server too.

Usage with react-redux

It's safe to use with the connect decorator as this can just be used as a way to pre-populate the redux store. Then you can use connect to map the store's state to props if you so wish.

Examples

See the example directory for a full blown example of all the above put into action.