unstated-retro v1.0.0
Unstated Retro
Retrofit your existing
unstatedcontainer. Feels likeunstated-next. Bridge the gap until you can swap.
Motivation
unstated-next is great for new projects, but if you've been using unstated for awhile, you probably already have some containers that aren't quick to rewrite and replace.
This package seeks to bridge that gap via a "child-first" migration. You can start rewriting child components to use hooks, and then eventually rewrite your container in the style of unstated-next. It aims to be API compatible with unstated-next for a smooth migration from (unstated + retro) to pure unstated-next.
Comparison to unstated and unstated-next
| unstated | unstated-next | unstated-retro | |
|---|---|---|---|
| Container | class CounterContainer extends Container | let Container = createContainer(customHook) | let RetroContainer = createRetroContainer(CounterContainer) |
| Provider | <Provider> | <Container.Provider> | <RetroContainer.Provider> |
| Subscribe | <Subscribe> | Container.useContainer() | RetroContainer.useContainer() |
| Tunnel | - | - | <RetroContainer.Tunnel> |
| React Version | ^15.0 | ^16.8 | ^16.8 |
The way to inject containers in unstated-retro matches the style of unstated-next.
| unstated | unstated-next | unstated-retro | |
|---|---|---|---|
| What is provided | Any Container class | The customHook passed to createContainer | The Container passed to createRetroContainer |
| Inject an instance | <Provider inject={[instance]}> docs | - | createContainer(instance) |
Install
npm install --save unstated-retroExample
import React, { useState } from "react"
import { render } from "react-dom"
import { Container } from 'unstated';
import { createRetroContainer } from "unstated-retro"
type CounterState = {
count: number,
}
class CounterContainer extends Container<CounterState> {
state = {
count: 0,
}
increment() {
this.setState({ count: this.state.count + 1 })
}
decrement() {
this.setState({ count: this.state.count - 1 })
}
}
let RetroContainer = createRetroContainer(CounterContainer)
function CounterDisplay() {
let counter = RetroContainer.useContainer()
return (
<div>
<button onClick={counter.decrement}>-</button>
<span>{counter.count}</span>
<button onClick={counter.increment}>+</button>
</div>
)
}
function App() {
return (
<RetroContainer.Provider>
<CounterDisplay />
<CounterDisplay />
</RetroContainer.Provider>
)
}
render(<App />, document.getElementById("root"))API
createRetroContainer(ContainerOrInstance)
class CounterContainer extends Container<CounterState> {
state = {
count: 0,
}
increment() {
this.setState({ count: this.state.count + 1 })
}
decrement() {
this.setState({ count: this.state.count - 1 })
}
}
let RetroContainer = createRetroContainer(CounterContainer)
// RetroContainer === { Provider, Tunnel, useContainer }Container.useContainer()
function ChildComponent() {
let input = RetroContainer.useContainer()
return <input value={input.value} onChange={input.onChange} />
}useContainer is designed to match the style of unstated-next
<Container.Tunnel>
let RetroContainer = createRetroContainer(CounterContainer)
// RetroContainer === { Provider, Tunnel, useContainer }
function ParentComponent() {
return (
<Provider>
<RetroContainer.Tunnel>
<CounterDisplay />
<CounterDisplay />
</RetroContainer.Tunnel>
<CounterDisplay />
</Provider>
)
}Bridges the context from a wrapper Provider via a Tunnel to a child using useContainer
<Container.Provider>
function ParentComponent() {
return (
<RetroContainer.Provider>
<CounterDisplay />
</RetroContainer.Provider>
)
}Replaces <Provider/> from unstated.
<Container.Provider> injected instance
const counter = new CounterContainer(); // Global instance
let RetroContainer = createRetroContainer(counter)
// RetroContainer === { Provider, Tunnel, useContainer }
function ParentComponent() {
return (
<RetroContainer.Provider>
<CounterDisplay />
</RetroContainer.Provider>
)
}How do I use an existing unstated container?
I need new shared state using an existing container
Use <RetroContainer.Provider> from unstated-retro in your new parent components, and useContainer in your new child components.
I am building a new child component
Use <RetroContainer.Tunnel> from unstated-retro in your existing parent components, and useContainer in your new child components.
I want to slowly migrate
- Use
unstated-retro- Create a
RetroContainerwithcreateRetroContainer(LegacyContainer) - Add
<RetroContainer.Tunnel>fromunstated-retroin your existing parent components
- Create a
- Start writing new child components using
useContainer. - Replace
<Subscribe/>- Migrate existing child components from
<Subscribe/>touseContainer.
- Migrate existing child components from
- Replace `
- Confirm all child components use
useContainerinstead of<Subscribe/> - Swap from
<Provider><RetroContainer.Tunnel>to just<RetroContainer.Provider>
- Confirm all child components use
- Migrate to
unstated-next- Confirm all parent components use
<RetroContainer.Provider>instead of<Provider> - Rewrite your
LegacyContaineras a hook - Switch from
createRetroContainertocreateContainer
- Confirm all parent components use
I'm building something completely new
Don't use this library, use unstated-next. Celebrate that you're not bogged down by supporting legacy unstated containers.
6 years ago