• Stars
    star
    1,785
  • Rank 26,056 (Top 0.6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 6 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

✍️ Immutable state with a mutable API

react-copy-write

goat

An immutable React state management library with a simple mutable API, memoized selectors, and structural sharing. Powered by Immer.

Overview

The benefits of immutable state are clear, but maintaining that immutable state can sometimes be burdensome and verbose: updating a value more than one or two levels deep in your state tree can require lots of object/array spreading, and it's relatively easy to accidentally mutate something.

react-copy-write lets you use straightforward mutations to update an immutable state tree, thanks to Immer. Since Immer uses the copy-on-write technique to update immutable values, we get the benefits of structural sharing and memoization. This means react-copy-write not only lets you use simple mutations to update state, but it's also very efficient about re-rendering.

Documentation

react-copy-write is currently under-going significant API changes as it's tested in a production environment. Most documentation has been removed until we arrive at a stable API. Below you will find a bare-bones API reference that should get you started.

createState

The default export of the package. Takes in an initial state object and returns a collection of components and methods for reading, rendering, and updating state.

import createState from 'react-copy-write'

const {
  Provider,
  Consumer,
  createSelector,
  mutate,
} = createState({name: 'Brandon' });

Provider

The Provider component provides state to all the consumers. All Consumer instances associated with a given provider must be rendered as children of the Provider.

const App = () => (
  <Provider>
    <AppBody />
  </Provider>
)

If you need to initialize state from props you can use the initialState prop to do so. Note that it only initializes state, updating initialState will have no effect.

const App = ({user}) => (
  <Provider initialState={{name: user.name }}>
    <AppBody />
  </Provider>
)

Consumer

A Consumer lets you consume some set of state. It uses a render prop as a child for accessing and rendering state. This is identical to the React Context Consumer API.

const Avatar = () => (
  <Consumer>
   {state => (
     <img src={state.user.avatar.src} />
   )}
  </Consumer>
)

The render callback is always called with a tuple of the observed state, using an array. By default that tuple contains one element: the entire state tree.

Selecting State

If a Consumer observes the entire state tree then it will update anytime any value in state changes. This is usually not what you want. You can use the select prop to select a set of values from state that a Consumer depends on.

const Avatar = () => (
  <Consumer select={[state => state.user.avatar.src]}>
    {src => <img src={src} />}
  </Consumer>
)

Now the Avatar component will only re-render if state.user.avatar.src changes. If a component depends on multiple state values you can just pass in more selectors.

const Avatar = () => (
  <Consumer select={[
    state => state.user.avatar.src,
    state => state.theme.avatar,
  ]}>
    {(src, avatarTheme) => <img src={src} style={avatarTheme} />}
  </Consumer>
)

Updating State

createState also returns a mutate function that you can use to make state updates.

const {mutate, Consumer, Provider} = createState({...})

Mutate takes a single function as an argument, which will be passed a "draft" of the current state. This draft is a mutable copy that you can edit directly with simple mutations

const addTodo = todo => {
  mutate(draft => {
    draft.todos.push(todo);
  })
}

You don't have to worry about creating new objects or arrays if you're only updating a single item or property.

const updateUserName = (id, name) => {
  mutate(draft => {
    // No object spread required 😍
    draft.users[id].name = name;
    draft.users[id].lastUpdate = Date.now();
  })
}

Check out the Immer docs for more information.

Since mutate is returned by createState you can call it anywhere. If you've used Redux you can think of it like dispatch in that sense.

Optimized Selectors

createState also returns a createSelector function which you can use to create an optimized selector. This selector should be defined outside of render, and ideally be something you use across multiple components.

const selectAvatar = createSelector(state => state.user.avatar.src);

You can get some really, really nice speed if you use this and follow a few rules:

Don't call createSelector in render.

🚫

const App = () => (
  // Don't do this 
  <Consumer select={[createSelector(state => state.user)]}>
    {...}
  </Consumer>
)

👍

// Define it outside of render!
const selectUser = createSelector(state => state.user);
const App = () => (
  <Consumer select={[selectUser]}>
    {...}
  </Consumer>
)

Avoid mixing optimized and un-optimized selectors

🚫

const selectUser = createSelector(state => state.user);
const App = () => (
  // This isn't terrible but the consumer gets de-optimized so
  // try to avoid it
  <Consumer select={[selectUser, state => state.theme]}>
    {...}
  </Consumer>
)

👍

const selectUser = createSelector(state => state.user);
const selectTheme = createSelector(state => state.theme);
const App = () => (
  <Consumer select={[selectUser, selectTheme]}>
    {...}
  </Consumer>
)

More Repositories

1

tinytime

⏰ A straightforward date and time formatter in <1kb
JavaScript
1,337
star
2

react-perimeter

🚧 Create an invisible perimeter around an element and respond when its breached.
JavaScript
1,048
star
3

alder

A minimal implementation of the UNIX tree command with colors!
JavaScript
225
star
4

json-to-graphql

Create GraphQL schema from JSON files and APIs
JavaScript
191
star
5

ln.js

TypeScript
175
star
6

critical-reasonmling-slides

OCaml
66
star
7

twitter-required-alt-text

A Chrome extension that will not let you tweet images until you've added alt text.
JavaScript
49
star
8

react-is-deprecated

Adds an optional isDeprecated method to React.PropTypes
JavaScript
44
star
9

enzyme-adapter-preact

JavaScript
39
star
10

reason-chat-app

A web-based chat application built with ReasonML
OCaml
36
star
11

ssr-benchmarks

Simple benchmark testing time taken to render on the server
JavaScript
31
star
12

reason-vue

OCaml
28
star
13

rst-selector-parser

A CSS selector-based parser for React Standard Tree (RST) traversal.
JavaScript
19
star
14

algorithms-and-data-structures

JavaScript
17
star
15

react-mood

render components based on the mood of the user
16
star
16

preact-flow-example

Minimal example showing how to use Preact with Flow
JavaScript
15
star
17

unnamed-web-lang

Rust
13
star
18

mentorship-program

A working draft of a mentorship program for underrepresented groups in tech.
11
star
19

blog

TypeScript
10
star
20

react-tester

A testing utility for modern React applications
JavaScript
10
star
21

reason-react-dom

OCaml
9
star
22

react-relay-redux

Redux integrations for Relay
JavaScript
8
star
23

gist.scss

gist.scss lets users restyle embedded gists easily with Sass variablles to better match their site.
CSS
8
star
24

babel-rust

A source-to-source JavaScript compiler written in Rust. Not for production use.
7
star
25

ReasonCharts

Rust
6
star
26

together

JavaScript
6
star
27

accessible-wordle

TypeScript
6
star
28

microbe.js

A small, easy to use Node.js framework written in ES6
JavaScript
6
star
29

preact-blaze

JavaScript
5
star
30

compiler

Rust
5
star
31

reaction-diffusion

JavaScript
4
star
32

unique-random-at-depth

JavaScript
4
star
33

gradient-animation.scss

Sass mixin that lets anyone easily implement an animated background gradient.
CSS
4
star
34

pymessage

Send SMS or iMessages directly from the command line
Python
4
star
35

reactive

JavaScript
3
star
36

cached-validations

Create validation functions that cache results
JavaScript
3
star
37

graphql-client

JavaScript
3
star
38

set-state-middleware

JavaScript
3
star
39

defined-keys-only

JavaScript
3
star
40

bst

JavaScript
3
star
41

assert-report

A barebones test reporter in ~1.5kb with zero dependencies
JavaScript
3
star
42

saveourshelter

source code for the upcoming website, saveourshelter.info
HTML
3
star
43

AccessibilityNodeInfoRepoProject

Kotlin
2
star
44

scroll.js

JavaScript
2
star
45

git-branch

A small script to create a new git branch and push it to origin
Shell
2
star
46

linked-list

JavaScript
2
star
47

except

blacklist object properties
JavaScript
2
star
48

googlefontloader

A web interface that allows for quick and easy downloads of the Google Web Fonts. It uses the Github API to access Google's public repository.
JavaScript
2
star
49

grasp.js

general purpose front-end utility for DOM manipulation and templating
JavaScript
2
star
50

duxly

A redux helper/pattern to simplify the mapping of action constants and reducers
JavaScript
2
star
51

react-spectre

A React component library for spectre.css
JavaScript
2
star
52

mkdirps-brackets

Allows for bracket expansions to be used in paths for folder creation
JavaScript
2
star
53

old-coding-challenge

Code challenge that must be completed by developer candidates
JavaScript
2
star
54

www

HTML
1
star
55

remix-remark-slate-resolve-bug

TypeScript
1
star
56

aweary

1
star
57

assets

CSS
1
star
58

chrome-user-timings-bug

HTML
1
star
59

exercism

My solutions to some exercism.io problems
Rust
1
star
60

ngGeometry

Angular directive for a simple Three.JS scene
JavaScript
1
star
61

immutable-state

helpers for managing immutable state
JavaScript
1
star
62

enzyme-test-repo

A repo to clone/fork when you need to provide a repo of an issue with enzyme
JavaScript
1
star