-
-
Save trueadm/4880c34dbf8af57a768ff5addb641550 to your computer and use it in GitHub Desktop.
// How we do this now | |
class CounterComponent extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
counter: 0, | |
}; | |
this._divRef = null; | |
this.increment = this.increment.bind(this); | |
} | |
increment() { | |
this.setState(state => ({ | |
counter: state.counter + 1, | |
})); | |
} | |
componentDidUpdate() { | |
this._divRef.color = this.props.color; | |
} | |
render() { | |
return ( | |
<div ref={divRef => this._divRef = divRef} onClick={this.increment}> | |
Count: {#state.counter} | |
</div> | |
); | |
} | |
} |
// How we could do this with React.Provider | |
// inspiration from Redux + Reason React | |
function counterReducer(action, state) { | |
switch (action) { | |
case "INCREMENT": { | |
return React.updateState({...state, {counter: counter + 1}}); | |
} | |
} | |
} | |
// a normal functional component | |
function CounterComponent(props) { | |
return ( | |
<React.Provider | |
initialState={() => ({ | |
counter: 0, | |
divRef: React.createRef(), | |
})} | |
reducer={counterReducer} | |
didUpdate={state => { | |
state.divRef.color = props.color; | |
}} | |
> | |
{(state, reduce) => | |
<div ref={state.divRef} onClick={() => reduce("INCREMENT"))}> | |
Count: {state.counter} | |
</div> | |
} | |
</React.Provider> | |
); | |
} |
// An alternative instead of the above | |
const CounterProvider = React.createProvider((action, state) => { | |
switch (action) { | |
case "INCREMENT": { | |
return React.updateState({...state, {counter: counter + 1}}); | |
} | |
} | |
}); | |
// a normal functional component | |
function CounterComponent(props) { | |
return ( | |
<CounterProvider | |
initialState={() => ({ | |
counter: 0, | |
divRef: React.createRef(), | |
})} | |
didUpdate={state => { | |
state.divRef.color = props.color; | |
}} | |
> | |
{(state, reduce) => | |
<div ref={state.divRef} onClick={() => reduce("INCREMENT"))}> | |
Count: {state.counter} | |
</div> | |
} | |
</CounterProvider> | |
); | |
} |
// An alternative instead of the above | |
const counterState = React.createStateReducer((action, state) => { | |
switch (action) { | |
case "INCREMENT": { | |
return React.updateState({...state, {counter: counter + 1}}); | |
} | |
} | |
}); | |
// a normal functional component | |
function CounterComponent(props) { | |
return counterState({ | |
initialState: () => ({ | |
counter: 0, | |
divRef: React.createRef(), | |
}), | |
didUpdate: state => { | |
state.divRef.color = props.color; | |
}, | |
render(state, reduce) { | |
return ( | |
<div ref={state.divRef} onClick={() => reduce("INCREMENT"))}> | |
Count: {state.counter} | |
</div> | |
); | |
} | |
}); | |
} |
// add a new component API, but suffers the same issues before in regards to folding | |
const CounterComponent = React.createReducerComponent({ | |
initialState: (props) => ({ | |
counter: 0, | |
divRef: React.createRef(), | |
}), | |
reducer: (action, state) => { | |
switch (action) { | |
case "INCREMENT": { | |
return React.updateState({...state, {counter: counter + 1}}); | |
} | |
} | |
}, | |
didUpdate: (props, state) => { | |
state.divRef.color = props.color; | |
}, | |
render: (props, state, reduce, context) => ( | |
<div ref={state.divRef} onClick={() => reduce("INCREMENT"))}> | |
Count: {state.counter} | |
</div> | |
), | |
}); |
So handling-stage5.js
might look very much like how components in React are today, so what do handling-stage2.js
, handling-stage3.js
or handling-stage4.js
offer? Well, when it comes to component folding, it makes a big difference.
Component folding works by recursively progressing through React component trees, starting with a "root" component. The "root" component's props
object is completely unknown at that point. Having a completely unknown object makes it much harder to do ahead of time optimizations as we have to assume everything is abstract until we find something that tells us something is "concrete". Concrete values allow us to statically resolve code ahead of time. Each time we come to a child component of the root that we can't fold (we call this a "bail out") we have to start a new tree, using the component that we couldn't fold as a root. That means, the props
of this component start of as unknown objects again.
With handling-stage2
, handling-stage3.js
and handling-stage4
, Providers
do not have a render, but rather their children operate in the closure of the parent – allowing us to continue using the evaluated props
of the parent – meaning far less bailing out will occur.
Ignore the naming of
Provider
in the above examples, it could be called something else with a better, more descriptive, less confusing name.handling-state3.js
allows for the reconciler to identify the sameProvider
is being used, which might be necessary to better handle lifecycle events. There are no "instance variables" in the above, insteadrefs
are now onstate
.The above examples allow for much better folding support via Prepack, even in nested branches, as the lifecycle events and state no longer needs to be "merged" to the root component and can instead live within the render tree.