really minimal wrap on new React.createContext() to provide context with a set function for Consumer
According to 0002-new-version-of-context, mutation is discouraged. But in some situations, Consumer really need a way to update the Provider. So, I come up with this module.
import createMutableContext from 'create-mutable-context' const { Provider, Consumer } = createMutableContext() <Provider defaultValue={1}> <Consumer> {ctx => { // ctx contain value and a set function return ( <button onClick={() => ctx.set(ctx.value + 1)}> {ctx.value} </button> ) }} </Consumer> </Provider>set(updater[, callback])set function interface is similar to react component's setState(), which accept updater as object or function.
updater as function with the signature:
(prevValue, providerProps) => newValueset will just replace the value. It will NOT merge newValue to prevValue.
you can also keep value in your own state (like Input)
import createMutableContext from 'create-mutable-context' const { Provider, Consumer } = createMutableContext() class App extends React.Component { state = { valueA: 1 } render() { return ( <Provider value={this.state.valueA} onChange={valueA => this.setState({ valueA })} > ... </Provider> ) } }Can use createStateMutext to mutate whole provider state instead of a value field only.
import { createStateMutext } from 'create-mutable-context' const C = createStateMutext({ foo: 1 }) const App = () => ( <C.Provider> <C.Consumer> {ctx => ( <button onClick={() => ctx.set({ foo: ctx.foo + 1 })} > {ctx.foo} </button> )} </C.Consumer> </C.Provider> )Consumers can observe part of changes easily by names. Names are auto calculate to bitmask
import { createObservableMutext } from 'create-mutable-context' const C = createObservableMutext({ foo: 0, bar: 0 }, { foo: {}, bar: {} }) const App = () => ( <C.Provider> <C.Consumer // observe to foo and only render if foo is changed observe="foo" > {ctx => `Foo: ${ctx.foo}`} </C.Consumer> <C.Consumer // observe to bar and only render if bar is changed observe="bar" > {ctx => `Bar: ${ctx.bar}`} </C.Consumer> <C.Consumer // observe to bar OR foo and render if bar OR foo are changed observe="bar,foo" > {ctx => `BarOrFoo: ${ctx.bar} ${ctx.foo}`} </C.Consumer> </C.Provider> )createMutableContext signature:
import createMutableContext from 'create-mutable-context' const options = { // init at the end of provider constructor providerConstruct: (provider) => {}, // init at the end of consumer constructor consumerConstruct: (consumer) => {}, // prepare ctx to pass to consumer children function consumerCtx: (ctx, consumer) => ctx, } createMutableContext(defaultValue, calculateChangedBits, option) const C = createMutableContext(defaultValue, null, { providerConstruct(provider) { Object.assign(provider.state, { inc1() { this.set(prevState => { value: prevState.value + 1 }) } }) }, }) const App = () => ( <C.Provider> <C.Consumer> {ctx => ( <button // can call inc1 onClick={ctx.inc1} > {ctx.value} </button> )} </C.Consumer> </C.Provider> )