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

@focuson/state

v1.44.3

Published

Uses the lens defined in @focuson/lens to implement state for tools such as react

Downloads

1,756

Readme

What is this project?

While react is a great project, react state management leaves much to be desired. Most comparisons of frameworks such as angular and react will list the issue that 'state management in react is difficult'.

Redux is one of the obvious candidates for state management, but it is difficult to use and full of boilerplate code. The pieces in redux are not easy to change or reuse. This project you are looking at now arose out of a refactoring of redux projects. By utilising a functional programming technique known as optics, and more specifically lens, much of the complexity of redux vanishes.

One of things I don't like about redux is that actions can do 'anything'. A second is that it is very hard to combine actions together: they are designed as 'atomic standalone components' rather than as 'composible unit'. The project gives the following benefits:

  • A much simpler and easier to read model of state management
  • Composability so it is as easy to plug the state management together as it is to plug the components together
  • Much stronger protection about what the equivalent of actions can and cannot do

With this project we have the idea that a lens is focused on a bit of the state. With this state management,components display a subset of the json (just like in redux), and components can normally change just that bit of the json (unlike redux where there is no such protection).

Dependencies

This project is very light weight. The only dependency is on the sister library the library that implements lens

Note that although it talks about react, there are no dependency on React or any other library or framework. This code is a library that can be used in any project with any other framework. All of the examples are currently in React simply because this project grew out of a refactoring of React/Redux code.

Size of code

This project and it's sister library are extremely small. The power in them is from the power in the idea behind lens. As much as possible has been removed from these libraries leaving just a single file in each that is itself quite small.

Getting started

Download

npm install @focuson/state

Related projects

This code uses the lens defined here. The README introduces Lens.

Tutorials

Examples

When should I use this project

This project isn't suitable for everything. It works best when the rendering and editing of a bit piece of state is split across multiple components. If there are many components that change many parts of state simultaneously then perhaps redux is better suited. If instead your display is split up with a 'editor component' that displays part of the state and lets you change that part of the state, then this project is the clear winner

Presentation

This presentation details how the react lens works

What is cool about Lens

They are composable and simple. Given a Lens<Main,Child> and Lens<Child,GrandChild> we can create a lens from Main to GrandChild.

let msgToCupLens = Lens.build<Msg>('msg').focusOn('order').focusOn('cup')
let cupToMadeofLens = Lens.build<Cup>('cup').focusOn('madeOf')
let msgToMadeOfLens = msgToCupLens.andThen(cupToMadeofLens)

Lens state

There are many uses cases (like a react gui) where there is a 'main json' and different components are responsible for different parts. LensState represents this. Internally it has the following

  • main... the main json
  • lens ... a lens to the bit of json we are interested
  • dangerouslySetMain This should not be directly called. It sets the state in the react application. Normally it is called by methods that provide cleaner access.

We use LensState extensively in our react state management. The most used methods/fields on the context are

  • json() returns the json that the context is focused on
  • setJson(j) Uses the lens to make a new 'main json'. Precisely what else happens is determined at the time the context is created. In the react statemanagement this causes a clean re-render
  • focusOn(fieldName) Returns a new context with a lens focused on the field name

The following shows how focusOn is used to create a context suitable for child components, and how we use .json() to access the component's json

export function SimpleGame<Main>({context}: GameProps<Main,GameData>) {
    return (
        <div className='game'>
            <NextMove context={context.focusOn('next')}/>
            <Board context={context.focusOn('board')}/>
        </div>)
}

export function NextMove<Main>({context}: GameProps<Main,NoughtOrCross>) {
    return (<div> Next Move{context.json()}</div>)
}

This example shows how we can use the setJson method. Note that if we wanted to we could inject the increment and decrement methods, but in this example I think the code is much cleaner as it is.

export function Counter<Main>({context}: LensProps<CounterDomain, Main, CounterData>) {
    let value = context.json().value
    let increment = () => context.setJson({value: value + 1})
    let decrement = () => context.setJson({value: value - 1})
    return (<p>Clicked: {value} times
            {' '}<button onClick={increment}>+</button>
            {' '}<button onClick={decrement}>-</button></p>)
}

More complicated methods

It is quite common to want to change two parts of the state simultaneously. For example if we are writting the tictactoe game 'click on a square', the lens for the component displaying the square is 'focusedon' the square. As well as wanting to change the square we also want to change the 'next value', so we want to update them both simultaneously: 'an atomic change'.

Here we are setting the two values explicitly

//Setting the values explicitly
let nextStateLens: Lens<GameData, NoughtOrCross> = Lenses.build<GameData>('game').focusOn('next')
let squareValue='X'
let nextValue= 'O'
context.useOtherLensAsWell<NoughtOrCross>(nextStateLens).setTwoValues(squareValue,nextValue)

It is more common to want to have the new values a function of the old. Here we see transformTwoValues.

let nextStateLens: Lens<GameData, NoughtOrCross> = Lenses.build<GameData>('game').focusOn('next')
let nextValueForSquare = (sq: NoughtOrCross, next: NoughtOrCross) => next;
let nextValueForNext = (sq: NoughtOrCross, next: NoughtOrCross) => invert(next);
context.useOtherLensAsWell<NoughtOrCross>(nextStateLens).transformTwoValues(nextValueForSquare, nextValueForNext)

Theoretical musings about quality

For me quality is about four things. I've included what is for me the reason I care about each thing. These qualities are all about 'how easy is it to change my software' and 'how easy is it to test my software'

  • Composability (how easy can I 'plug these things together'?)
  • Decoupling (how many lines of code are impacted when I make a change?)
  • Cohesion (how many windows do I have to open to see one aspect of the code?)
  • Readability of the result (Can I come back in six months and still understand it?)

In all but decoupling I feel this state management is a clear improvement over redux.When it comes to decoupling my experience in the projects I have looked at, this lens approach is usually better as most redux actions become bound to the structure. However there are projects I can visualise where redux would be more decoupled (especially if the redux actions actually used lens)

Certainly the ability to test the code is (in my experience) far easier than with redux.

Where are the tests?

To keep the projects small, the tests have been moved here

The tests in the example projects act as integration tests as well