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-setstate-connect

v4.0.0

Published

Helper function to connect React's component to a redux-like store using functional setState.

Downloads

25

Readme

react-setstate-connect

This javascript module contains 2 helper functions:

  • connect: Is a HOC function that wraps React components and allows you to inject props and actions function from a redux-like state management construct.
  • serverState: Allows you to use the same state management construct on the server. Especially useful for server side rendering.

Version 4.0.0

  • All state and actions are merged and passed together in the components properties
  • This version lacks flow type definitions. (Future versions will reimplement them in a more simple way)

Version 3.0.0 DEPRECATED VERSION

Sadly we had to deprecate this short lived version.

Version 2.1.0 With Flow type definitions

This version of react-setconnect-state includes flow type definitions for the basic state manager construct.

How to use it:

ES6 imports

import connect from 'react-setstate-connect'
import serverState from 'react-setstate-connect/lib/server'

or

const connect = require('react-setstate-connect')
const serverState = require('react-setstate-connect/lib/server')

You can put your state management code in a single file, you will need a reducer function in the form (state, action) => state, a createActions function in the form ({getState, dispatch}) => {actions}, also an initialState object (if not declared an empty object will be used).

state.js


export default () => ({
  initialState: {
    value: 0
  },

  reducer: (state, action) => {
    switch (action.type) {
      case 'INCREASE': return {...state, value: state.value + action.value}
      case 'DECREASE': return {...state, value: state.value - action.value}
      default: return state
    }
  },

  createActions: ({getState, dispatch}) => {
    const increase = value => dispatch('INCREASE', {value})
    const decrease = value => dispatch('DECREASE', {value})
    const increase1 = () => dispatch('INCREASE', {value: 1})
    const decrease1 = () => dispatch('DECREASE', {value: 1})

    const delayedIncrease = () => new Promise(
      resolve => setTimeout(() => resolve(increase(1)), 1000)
    )

    return {
      increase,
      decrease,
      increase1,
      decrease1,
      delayedIncrease
    }
  }
})

This example module is exporting a single function that creates an object with 3 properties reducer, the reducer function; createActions, a function that returns a object containing functions an initialState also an object.

createActions receives two properties inside an object: getState and dispatch

  • getState: Is a function that returns the current store state. Ex. getState()
  • dispatch: Is a function that always return a Promise and allows you to dispatch actions to the reducer. Ex. dispatch(ACTION_TYPE, {key: 'value'})

...and wrap your component with the connect function

ValueButton.js

import connect from 'react-setstate-connect'
import createState from './state.js'

const ValueButton = ({
  value,
  increase1,
  decrease1,
  delayedIncrease
}) => (
  <div>
    <button onClick={increase1}>Add 1</button>
    <button onClick={delayedIncrease}>Add 1 later</button>
    <p>{value}</p>
    <button onClick={decrease1}>Sub 1</button>
  </div>
)

export default connect(ValueButton, createState())

Auto merge props

Once wrapped our component we might still want to pass props to it, those properties are merged in overwriting the initial state and passed down in the state prop.

import dataPull from './data-puller'

const DataView = ({
  data,
  error,
  pullingData
}) => (
  <div>...</div>
)

const ConnectedData = connect(DataView, dataPull())

// state.data = default initial state
<ConnectedData />

// state.data = preloadedData
<ConnectedData data={preloadedData} />

Server side rendering and tests

Many web apps try to implement Server Side Render by reusing the same state management functions. react-setstate-connect constructs can be used on the server via a small helper function we included serverState. This function can also be used for testing state management.

const serverState = require('react-setstate-connect/lib/server')
const manageState = require('./state')

const server = serverState(manageState())

return server.actions.loadAsyncData()
    .then(() => {
        // Do something with state.data
        const currentState = server.getState()
        // ...
    })

// You can also chain async actions.

const server = serverState(manageState())
server.actions.loadAsyncData()
  .then(() => server.actions.doOtherAsyncTask())
  .then(() => server.actions.yetAnotherAsyncTask(server.getState().someProp))
  .then(() => {
    console.log(server.getState())
  })

Next.js example

Next.js (https://github.com/zeit/next.js) is a "Framework for server-rendered or statically-exported React apps" they implemented a fairly simple way to inject server side data to your React apps via adding an async getInitialProps function in your page root component. This function can receive Request/Response object like any HTTP middleware function. So, it can check for cookies, make redirects, etc.

Assuming we have a Next.js app with a page that connects to a state management construct that loads data async using on action function. We can do:

_state/page-state.js

import axios from 'axios'

const LOADED_DATA = 'LOADED_DATA'

export default () => ({
    initialState: {data: null}
    reducer: (state, action) => {
        switch (action.type) {
            case LOADED_DATA: return {...state, data: action.data}
        }

        return state
    },
    createActions: ({dispatch}) => ({
        loadData: () => axios.get('/api/data')
            .then(response => response.data, () => []) // Added error handling here, just returns an empty array
            .then(data => dispatch(LOADED_DATA, {data})
    })
})

pages/page.js

import React from 'react'
import connect from 'react-setstate-connect'
import server from 'react-setstate-connect/lib/server'
import managePageState from '../state/page-state'
import { Panel, DataList, Button } from '../components'

const Page = ({
    data,
    loadData
}) => (
    <Panel>
        <DataList data={data} />

        <Button onClick={loadData}>
            Refresh List
        </Button>
    </Panel>
)

const ConnectedPage = connect(Page, managePageState())

// Server-Side-Render portion, will initialize state manager, call async function and return value as initial props.
ConnectedPage.getInitialProps = () => {
  const ssr = server(managePageState())

  return ssr.actions.loadData()
    .then(() => ssr.getState())
}

export default ConnectedPage

On the server, you should take care of cookies and using the correct URL for the api endpoint. But, since the state management construct is a function we can make it to accept those as parameters and use it accordingly.

_state/page-state.js

import axios from 'axios'

const LOADED_DATA = 'LOADED_DATA'

export default (cookies, urlBase) => ({
    ...
    createActions: ({dispatch}) => ({
        loadData: () => axios.get('${urlBase}/api/data', {headers: {...}})
         ...
    })
})

Anything is possible since is just a function.

Pro tips

  • Keep the state management code and the wrapped component close together.
  • By default all actions in the form (props) => dispatch(type, props) return promises, so, is a good thing to make all async actions return promises as well.
  • Your wrapped component should never need to use setState directly, if you find yourself adding this.setState then go to the actions creator and reducer and add it there.
  • Composition is possible by using the connect wrapper more that once. Each layer will add its own Connected component which holds the state and pass it down as properties. This properties will become the initial state (along with the passed initialState param in the connect function) of the next wrapped component.
  • Lift up/down state.
  • Create parametrized state handling functions, those are easier to tests and reuse

Read more