DEV Community

Cover image for Easy State Management on ReactJS with flatstore
Joel Ruiz
Joel Ruiz

Posted on

Easy State Management on ReactJS with flatstore

State management is the biggest concern for any developer when starting a new app. The few choices available (redux/mobx) are designed for enterprise-level development, where state management follows a strict pattern.

Pfffft... Who needs all that for a quick prototype or medium sized application.

flatstore -- ReactJS state management at the speed of light. Get/Set state from anywhere with component re-render.


Let's build an example React app

Alt Text

The example above shows dev.to articles with a random page button using the API
https://dev.to/api/articles?page=1

The rest of this article will focus on building the simple app using flatstore. We will build 4 files to put all of this together.

  • DevAPI.js
  • Articles.js
  • Article.js
  • PageWidget.js

This flatiron example and more can be found here
https://github.com/joetex/flatstore-examples


DevAPI.js

import flatstore from 'flatstore';
import axios from 'axios';

flatstore.set("articles", []);
flatstore.set("page", 1);

export default async function apiDevArticles() {
    let randomPage = Math.floor(Math.random() * Math.floor(20)) + 1;
    flatstore.set("page", randomPage);

    try {
        let response = await axios.get('https://dev.to/api/articles?page=' + randomPage);
        flatstore.set("articles", response.data);
    }
    catch (err) {
        console.log(err);
    }
}

At the top, we define the default values using flatstore.set.

A simple function then uses axios to GET request the dev.to API. You no longer need to follow any programming patterns. This API function can be created anywhere, but for organizational purposes, this was created in its own file.

The random page number is stored in page, and the API response (JSON Array) is stored to articles in the flatstore.


Articles.js

class Articles extends React.Component {
    constructor(props) {
        super(props);
        apiDevArticles();
    }
    render() {
        return (
            <div>
                <h2>dev.to Articles</h2>
                <PageWidget />
                {this.props.articles.map((article, index) => (
                    <Article id={index}></Article>
                ))}
            </div>
        );
    }
}
export default flatstore.connect(['articles'])(Articles);

This file calls the API in the constructor (or anywhere you want!), loops through all the articles, and renders an Article component passing the array index to id property which we will use soon.

flatstore.connect is where we begin seeing how components can hook into the data. It may have up to 3 parameters (example in Article.js):

  1. An array of string keys to automatically watch from the store.
  2. function onCustomWatched for generating the same array as #1 with code.
  3. function onCustomProps for customizing the actual key/value stored in this.props of the component.

The data is automatically stored into this.props.articles matching the key name used by both apiDevArticles and flatstore.connect.


Article.js

class Article extends React.Component {
    render() {
        return (
            <div>
                <a href={this.props.url}>
                    <img src={this.props.social_image} width="75" />
                    <strong>{this.props.title}</strong>
                </a>
                <br />
                <span>{this.props.description}</span>
                <br /><br />
            </div>
        );
    }
}

let onCustomWatched = (ownProps) => {
    return ['articles-' + ownProps.id];
}
let onCustomProps = (key, value, store, ownProps) => {
    return { ...value }
}
export default flatstore.connect([], onCustomWatched, onCustomProps)(Article);

Now we can render each article.

You'll notice flatstore.connect is using two new parameters that are callback functions.

onCustomWatched returns an array of store keys to watch, and using the components own properties for id, we can specify we only want to watch for articles[id] in the store, incase only that object changes.

Note: You can drill down to a specific child of an array or object using the syntax {object}-{key} or {array}-{index}. For example: articles-1, will return articles[1] from the store. Or go crazy with something like articles-1-tag_list-0

onCustomProps is where you define the structure of the value that gets appended into this.props. In this example, we just spread all the article data into this.props, so it can be accessed like this.props.title or this.props.description.


PageWidget.js

class PageWidget extends React.Component {
    render() {
        return (
            <div>
                <button onClick={() => { apiDevArticles() }}>Random Page</button>
                <span>Page #{this.props.page}</span>
            </div>
        )
    }
}
export default flatstore.connect(["page"])(PageWidget);

Simple usage again. Just watch the store for "page" changes, also call the API again when clicking the button.


Whew! I hope that was simple... I spent more time writing this article than coding that react app.

If you have any questions or suggestions to improve flatstore, please feel free to leave a comment or send me a pull request.

Enjoy!

Discussion (0)