Skip to content

About

Redux middleware that detects mutations between and outside redux dispatches. For development use only.

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

redux-immutable-state-invariant (Go)

A Go port of redux-immutable-state-invariant.

Redux-style middleware that panics with an error when you try to mutate your state either inside a dispatch or between dispatches. For development use only!

Why?

Because you're not allowed to mutate your state in your reducers! And by extension, you shouldn't mutate it outside them either. To change state in your app you should always return a new instance of your state with the changes applied.

If you use persistent/immutable data structures this is handled for you. But if you use plain objects and arrays (modeled here by *invariant.Obj and *invariant.Arr), you need to be careful to avoid mutations — and this middleware helps you catch them.

Value model

Go has no single dynamic "any JSON value" type with JavaScript's reference semantics, so this port models state with a small Value type set that mirrors JS reference identity (===):

  • *invariant.Obj — an ordered string-keyed object (containers compared by pointer identity, like JS objects).
  • *invariant.Arr — an ordered list (compared by pointer identity, like JS arrays).
  • invariant.Str, invariant.Num, invariant.Bool — primitives (compared by value).
  • invariant.Null, invariant.Undefined — the null / undefined singletons.
  • invariant.NaN() — a NaN number; like JS, NaN never equals itself.

*Obj preserves insertion order of keys and offers Set/Get/Delete/Keys/ Clone; *Arr offers Push/Len/At/SetAt/Keys/Clone.

How to install

go get github.com/leoasis/redux-immutable-state-invariant

This is intended for use only during development. It copies and walks your state on every dispatch and will degrade performance — do not use it in production.

How to use

Add it as middleware in your (Redux-style) store setup. The middleware has the shape func(MiddlewareAPI) func(next Dispatch) Dispatch:

import invariant "github.com/leoasis/redux-immutable-state-invariant"

mw := invariant.New(nil) // nil options == all defaults

api := invariant.MiddlewareAPI{GetState: func() invariant.Value { return store.State() }}
dispatch := mw(api)(next) // next is your base dispatch

If you're doing things correctly you'll see nothing different. But if you mutate your state somewhere — inside a dispatch or between dispatches — the middleware panics with an *invariant.InvariantError carrying a descriptive message (including the mutated path, e.g. `foo.bar.3`).

A complete runnable example lives in example/main.go; run it with:

go run ./example

API

func New(options *Options) Middleware

Creates the middleware. Passing nil is equivalent to {} in JS and selects all defaults. Options customizes behavior:

  • IsImmutable func(Value) bool — specify whether a value should be treated as immutable. The default (IsImmutableDefault, selected when this is nil) returns true for primitives (Str, Num, Bool, Null, Undefined) and false for *Obj/*Arr. Useful if some of your state uses persistent structures you want the middleware to skip.

  • Ignore []string — branch(es) of the state object to ignore when detecting mutations. Each element is a dot-separated "path" string matching named nodes from the root state.

    // ignore mutation detection along the 'foo' & 'bar.thingsToIgnore' branches
    mw := invariant.New(&invariant.Options{
        Ignore: []string{"foo", "bar.thingsToIgnore"},
    })

trackForMutations (lower-level API)

invariant.TrackForMutations(isImmutable, ignore, obj) returns a *Tracker whose DetectMutations() reports whether the tracked object was mutated since construction, returning a MutationResult{WasMutated bool, Path []string}. This is the primitive the middleware is built on and is exported for direct use and testing.

Differences from the JS original

  • Thrown JS Errors become panics with *invariant.InvariantError (message prefixed Invariant Violation: , identical text otherwise).
  • JS objects/arrays are modeled by the Value types described above to preserve reference-identity semantics.
  • The circular-safe action serialization used in the inside-dispatch message is a port of json-stringify-safe (circular references render as [Circular ~]).

Behavior is otherwise identical; the full original test suite (8 middleware + 25 tracker cases) is ported to Go and passes.

License

MIT — see LICENSE.md. Copyright (c) 2015 Leonardo Andres Garcia Crespo.

About

Redux middleware that detects mutations between and outside redux dispatches. For development use only.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages