Uses the lens defined in @focuson-nw/lens to implement state for tools such as react
npm install @focuson-nw/stateWhile 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).
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.
``shell``
npm install @focuson-nw/state
This code uses the lens defined here. The
README introduces Lens.
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
This presentation details how the react lens works
They are composable and simple. Given a Lens and Lens we can create a lens from MainGrandChild
to .
`typescript`
let msgToCupLens = Lens.build
let cupToMadeofLens = Lens.build
let msgToMadeOfLens = msgToCupLens.andThen(cupToMadeofLens)
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 jsonlens
* ... a lens to the bit of json we are interesteddangerouslySetMain
* 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 onsetJson(j)
* Uses the lens to make a new 'main json'. Precisely what else happens is determined at the time thefocusOn(fieldName)
context is created. In the react statemanagement this causes a clean re-render
* 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 jsontypescript jsx
export function SimpleGame
return (
)
}
export function NextMove
return ( Next Move{context.json()})
}
`
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.
` Clicked: {value} timestypescript jsx`
export function Counter
let value = context.json().value
let increment = () => context.setJson({value: value + 1})
let decrement = () => context.setJson({value: value - 1})
return (
{' '}
{' '}
}
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
`typescript`
//Setting the values explicitly
let nextStateLens: Lens
let squareValue='X'
let nextValue= 'O'
context.useOtherLensAsWell
It is more common to want to have the new values a function of the old. Here we see transformTwoValues. `typescript``
let nextStateLens: Lens
let nextValueForSquare = (sq: NoughtOrCross, next: NoughtOrCross) => next;
let nextValueForNext = (sq: NoughtOrCross, next: NoughtOrCross) => invert(next);
context.useOtherLensAsWell
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.
To keep the projects small, the tests have been moved here
The tests in the example projects act as integration tests as well