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

v0.4.0

Published

State management agnostic decorator for loosely coupled React containers

Downloads

18

Readme

React Connect

State management agnostic decorator for loosely coupled React containers

or

Better way to connect React component to an arbitrary store

or

Alternative to react-redux, mobx-react and similar packages

Motivation

Let's start with redux containers

A typical redux container looks something like this:

const Container = connect(
    state => ({
        someProps: state.someState
    }),
    dispatch => ({
        onClick() {
            dispatch({type: 'SOME_ACTION'});
        }
    })
)(props => (
    <div onClick={props.onClick}>{props.someProps}</div>
));

Note that you tightly coupled your component with a redux store. Even if you used selectors which hide store internals, these selectors expect that state object of certain shape will always be provided. If some day you decide to switch to mobx, or you'll just want to use the same container in different redux app, you might have a problem.

Not a problem! - you say, since you still can export raw, stateless component itself, or even get access to it via Container.WrappedComponent, where it is always exposed.

That is true, but in complex applications, especially those trying to improve their rendering performance, you will often find yourself making large number of small containers, which themselves will be rendered inside other containers. In fact, to efficiently render list of elements in redux, the best way is to make each element of a list container itself:

export const List = ({list}) => (
    <ol>
    {list.map(elementId => {
        // Element is a redux container!
        return (<Element id={elementId} key={elementId} />);
    })}
    </ol>
);

Note that even though List is exported as a pure component, at this point it is impossible to render it without providing redux store in context. As was mentioned before, this component is not even tied to redux library in general, but to a state object of very specific shape.

Generally speaking, from now on List component will not be usable in any other application, even redux one.

This is very bad place to be. React promises deep componentization - if I created List of Element components once, I should not have to write it again ever. I should be able to just take it and render it in any other application, whether it's using redux, mobx or any other state management solution (even totally custom one). I should be able to create small, deeply nested containers to not take performance and readability penalty of having just one, big container at the root of the app, while still having my containers not tied to any specific data structure or library and being able to share them between projects without any additional work (this is what we mean, when we say "components" after all).

react-connect allows to do just that. It preserves the notion of container, but with a twist. Here is redux container from first snippet, rewritten to react-connect:

import { container } from 'react-connect';

const Container = container('Container', props => (
    <div onClick={props.onClick}>{props.someProps}</div>
));

It actually looks very much like regular component. So how you feed it data? Where did these redux mappers go?

Let's say I want to connect this container to redux store. What I need is a link between store and my container:

import { reduxLink } from 'react-connect';

const containerLink = reduxLink({
    mapStateToProps: (state) => ({
        someProps: state.someState
    }),
    mapDispatchToProps: (dispatch) => ({
        onClick() {
            dispatch({type: 'SOME_ACTION'});
        }
    })
});

Nothing new here. Just our redux mappers, which we wrapped in reduxLink function. This function returns what we call a link. Note that this link might land in the same file as our container, but it is probably good idea to keep it somewhere else (say in links folder, where each link will be in a file matching name of container that it provides data for).

This way even though we defined how to connect container to store, container is still free of any dependencies to this store or even redux in general. We can write multiple links to the very same container and choose relevant one when we render our app.

Let's do just that:

import { Links, Provider } from 'react-connect';

const store = createStore(someReducer); // Just regular redux store here.
const links = new Links();

links.addLink(Container, containerLink);

render(
    <Provider links={links} context={{store}} >
        <Container />
    </Provider>
)

This again looks similar to how you would render redux app. Novelty is Links object via which you specify which container will be fed data with which links. You then use Provider, where you put defined links and - in case of redux app - you provide store in context.

Bear in mind that Provider and Links themselves still have no idea that you make redux app. This elements will not change when rendering mobx or any other map. Sometimes you will just put something else in context (if it's needed). The real change is writing and applying other links to a component.

Feeding container other data

So did we achieve anything? We had to do a little bit of extra work and our app got more complex with yet another concept of links and additional folder with them.

Let's start with the simplest case. Let's just render container with some static data. We might need it for an entry in storybook (just to see how it looks during styling) or for snapshot tests.

As was said earlier our List with Element components was impossible to render without redux store in context. Let's rewrite it to react-connect and try render it with some static data:

const Element = container('Element', props => (
    <li>{props.elementName}</li>
));

const List = container('List', props => (
    <ol>
    {list.map(elementId => <Element id={elementId} key={elementId} />)}
    </ol>    
));

Before we used reduxLink to connect container with data. But we can simply pass data as an object:

links
    .addLink(Element, { elementName: 'some name' })
    .addLink(List, { list: [1, 2, 3, 4] });

Because every container can accept links - just like Provider - you can now render List:

render(<List links={links} />);

This will result in following markup:

<ol>
    <li>some name</li>
    <li>some name</li>
    <li>some name</li>
    <li>some name</li>
</ol>

Because we passed static props as a link to Element, every component has the same content. What if we wanted to render real list?

You can provide function instead of static object. Function will receive own props with which component was rendered. In case of Element - id prop. We can use this id to access real data from list:

const list = ['first name', 'second name', 'third name'];

links
    .addLink(List, { list: list.map((_, id) => id) })
    .addLink(Element, ({id}) => ({ elementName: list[id] }));

List component gets a list of element ids, which are then used to retrieve data from the list.

This results in a markup:

<ol>
    <li>first name</li>
    <li>second name</li>
    <li>third name</li>
</ol>

So we rendered container without the need for redux store, or any kind of high concept state management library for that matter.