SlideShare a Scribd company logo
React, The Inglorious Way
Matteo Antony Mistretta
Inglorious Coderz
@antonymistretta
Why
React is evolving rapidly
A few rules, lots of strategies
Learning them makes us better coders
antony@ingloriouscoderz ~> whoami
Agenda
Class Components
Container/Presentational
Higher-Order Components
Render Props
Hooks
42
-1 +1
class Counter extends Component {
constructor(props) {
super(props)
this.state = { count: props.initialCount }
this.increment = this.increment.bind(this)
}
increment() {
this.setState({ count: this.state.count + 1 })
}
decrement() {
this.setState({ count: this.state.count - 1 })
}
render() {
const { count } = this.state
return (
<div>
<h1>{count}</h1>
<div className="input-group">
<button onClick={this.decrement.bind(this)}>-1</button>
<input
type="number"
value={count}
onChange={event => {
this.setState({ count: parseInt(event.target.value) })
}}
/>
<button onClick={this.increment}>+1</button>
</div>
</div>
)
}
}
render(<Counter initialCount={42} />)
42
42
-1 +1
class Counter extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
const { count } = this.state
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={this.decrement}>-1</button>
<input type="number" value={count} onChange={this.handleChange} />
<button onClick={this.increment}>+1</button>
</div>
</>
)
}
}
render(<Counter initialCount={42} />)
42
Agenda
Class Components
Container/Presentational
Higher-Order Components
Render Props
Hooks
42
-1 +1
class Counter extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
const { count } = this.state
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={this.decrement}>-1</button>
<input type="number" value={count} onChange={this.handleChange} />
<button onClick={this.increment}>+1</button>
</div>
</>
)
}
}
render(<Counter initialCount={42} />)
42
42
-1 +1
class CounterContainer extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
return (
<Counter
count={this.state.count}
increment={this.increment}
decrement={this.decrement}
handleChange={this.handleChange}
/>
)
}
}
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<CounterContainer initialCount={42} />)
42
42
-1 +1
class CounterContainer extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
// NOTE: this is bad
return Counter({
count: this.state.count,
increment: this.increment,
decrement: this.decrement,
handleChange: this.handleChange,
})
}
}
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<CounterContainer initialCount={42} />)
42
Agenda
Class Components
Container/Presentational
Higher-Order Components
Render Props
Hooks
42
-1 +1
class CounterContainer extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
return (
<Counter
count={this.state.count}
increment={this.increment}
decrement={this.decrement}
handleChange={this.handleChange}
/>
)
}
}
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<CounterContainer initialCount={42} />)
42
42
-1 +1
const withCounter = Enhanced =>
class CounterContainer extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
return (
<Enhanced
count={this.state.count}
increment={this.increment}
decrement={this.decrement}
handleChange={this.handleChange}
/>
)
}
}
Counter = withCounter(Counter)
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<Counter initialCount={42} />)
42
42
-1 +1
const enhance = compose(
withState('count', 'setCount', ({ initialCount }) => initialCount),
withHandlers({
increment: ({ setCount }) => () => setCount(count => count + 1),
decrement: ({ setCount }) => () => setCount(count => count - 1),
handleChange: ({ setCount }) => event =>
setCount(parseInt(event.target.value)),
}),
pure,
)
Counter = enhance(Counter)
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<Counter initialCount={42} />)
42
Agenda
Class Components
Container/Presentational
Higher-Order Components
Render Props
Hooks
Hello world!
function Parent() {
return (
<Wrapper>
<Child who="world" />
</Wrapper>
)
}
function Child({ who }) {
return `Hello ${who}!`
}
function Wrapper({ children }) {
return <h1>{children}</h1>
}
render(Parent)
Hello
WORLD!
function Parent() {
return <Wrapper who="world" Component={Child} />
}
function Child({ who }) {
return `Hello ${who}!`
}
function Wrapper({ who, Component }) {
const shoutedWho = who.toUpperCase()
return (
<h1>
<Component who={shoutedWho} />
</h1>
)
}
render(Parent)
Hello
WORLDz!
function Parent() {
return <Wrapper who="world" render={who => <Child who={who + 'z'} />} />
}
function Child({ who }) {
return `Hello ${who}!`
}
function Wrapper({ who, render }) {
const shoutedWho = who.toUpperCase()
return <h1>{render(shoutedWho)}</h1>
}
render(Parent)
Hello
WORLDz!
function Parent() {
return <Wrapper who="world">{who => <Child who={who + 'z'} />}</Wrapper>
}
function Child({ who }) {
return `Hello ${who}!`
}
function Wrapper({ children, who }) {
const shoutedWho = who.toUpperCase()
return <h1>{children(shoutedWho)}</h1>
}
render(Parent)
42
-1 +1
class CounterContainer extends PureComponent {
state = { count: this.props.initialCount }
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
render() {
return (
<Counter
count={this.state.count}
increment={this.increment}
decrement={this.decrement}
handleChange={this.handleChange}
/>
)
}
}
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<CounterContainer initialCount={42} />)
42
42
-1 +1
class CounterContainer extends PureComponent {
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
state = {
count: this.props.initialCount,
increment: this.increment,
decrement: this.decrement,
handleChange: this.handleChange,
}
render() {
return this.props.children(this.state)
}
}
function Counter({ initialCount }) {
return (
<CounterContainer initialCount={initialCount}>
{({ count, increment, decrement, handleChange }) => (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)}
</CounterContainer>
)
}
render(<Counter initialCount={42} />)
42
42
-1 +1
class CounterContainer extends PureComponent {
increment = () => this.setState(({ count }) => ({ count: count + 1 }))
decrement = () => this.setState(({ count }) => ({ count: count - 1 }))
setCount = count => this.setState({ count })
handleChange = event => this.setCount(parseInt(event.target.value))
state = {
count: this.props.initialCount,
increment: this.increment,
decrement: this.decrement,
handleChange: this.handleChange,
}
render() {
return this.props.children(this.state)
}
}
class Counter extends PureComponent {
renderCounter = ({ count, increment, decrement, handleChange }) => (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
render() {
return (
<CounterContainer initialCount={this.props.initialCount}>
{this.renderCounter}
</CounterContainer>
)
}
}
render(<Counter initialCount={42} />)
42
Agenda
Class Components
Container/Presentational
Higher-Order Components
Render Props
Hooks
They separate stateful logic
They are composable functions
They allow us to go fully functional
They keep our component hierarchy flat
42
-1 +1
const enhance = compose(
withState('count', 'setCount', ({ initialCount }) => initialCount),
withHandlers({
increment: ({ setCount }) => () => setCount(count => count + 1),
decrement: ({ setCount }) => () => setCount(count => count - 1),
handleChange: ({ setCount }) => event =>
setCount(parseInt(event.target.value)),
}),
pure,
)
Counter = enhance(Counter)
function Counter({ count, increment, decrement, handleChange }) {
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<Counter initialCount={42} />)
42
42
-1 +1
function useCounter(initialCount) {
const [count, setCount] = useState(initialCount)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
const handleChange = event => setCount(parseInt(event.target.value))
return { count, increment, decrement, handleChange }
}
Counter = memo(Counter)
function Counter({ initialCount }) {
const { count, increment, decrement, handleChange } = useCounter(initialCount)
return (
<>
<h1>{count}</h1>
<div className="input-group">
<button onClick={decrement}>-1</button>
<input type="number" value={count} onChange={handleChange} />
<button onClick={increment}>+1</button>
</div>
</>
)
}
render(<Counter initialCount={42} />)
42
Which to use?
Matteo Antony Mistretta - React, the Inglorious way - Codemotion Amsterdam 2019
Matteo Antony Mistretta - React, the Inglorious way - Codemotion Amsterdam 2019
Matteo Antony Mistretta - React, the Inglorious way - Codemotion Amsterdam 2019
Hello
WORLD!
function Hello({ who }) {
return <h1>{`Hello ${who}!`}</h1>
}
const enhance = Enhanced => ({ who, ...props }) => {
if (!who.length) return 'Name too short'
const shoutedWho = who.toUpperCase()
return <Enhanced {...props} who={shoutedWho} />
}
Hello = enhance(Hello)
render(<Hello who="world" />)
world
though
ly
tual
issey
const SimpleList = ({ whos }) => (
<ul style={styles.list}>
{whos.map(who => (
<li>{who}</li>
))}
</ul>
)
const ComplexList = ({ renderEven, renderOdd, whos }) => (
<ul style={styles.list}>
{whos.map((who, index) => (index % 2 ? renderEven(who) : renderOdd(who)))}
</ul>
)
const Parent = ({ whos, simple }) => {
if (simple) return <SimpleList whos={whos} />
return (
<ComplexList
renderEven={who => <li style={styles.even}>{'Even ' + who}</li>}
renderOdd={who => <li style={styles.odd}>{'Odd ' + who}</li>}
whos={whos}
/>
)
}
render(
<Parent whos={['world', 'though', 'ly', 'tual', 'issey']} simple={true} />,
)
const styles = {
list: { textAlign: 'left' },
odd: { color: 'grey' },
even: { color: 'cornflowerblue' },
}
Which to use?
Classes: getSnapshotBeforeUpdate / componentDidCatch
HOCs: proxy / before render (change props, prevent renders)
Render Props: mix logic / multiple sub-items / switch behavior
Hooks: everything else
Thank you.
Questions?
html | pdf | source

More Related Content

Matteo Antony Mistretta - React, the Inglorious way - Codemotion Amsterdam 2019

  • 1. React, The Inglorious Way Matteo Antony Mistretta Inglorious Coderz @antonymistretta
  • 2. Why React is evolving rapidly A few rules, lots of strategies Learning them makes us better coders
  • 5. 42 -1 +1 class Counter extends Component { constructor(props) { super(props) this.state = { count: props.initialCount } this.increment = this.increment.bind(this) } increment() { this.setState({ count: this.state.count + 1 }) } decrement() { this.setState({ count: this.state.count - 1 }) } render() { const { count } = this.state return ( <div> <h1>{count}</h1> <div className="input-group"> <button onClick={this.decrement.bind(this)}>-1</button> <input type="number" value={count} onChange={event => { this.setState({ count: parseInt(event.target.value) }) }} /> <button onClick={this.increment}>+1</button> </div> </div> ) } } render(<Counter initialCount={42} />) 42
  • 6. 42 -1 +1 class Counter extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { const { count } = this.state return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={this.decrement}>-1</button> <input type="number" value={count} onChange={this.handleChange} /> <button onClick={this.increment}>+1</button> </div> </> ) } } render(<Counter initialCount={42} />) 42
  • 8. 42 -1 +1 class Counter extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { const { count } = this.state return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={this.decrement}>-1</button> <input type="number" value={count} onChange={this.handleChange} /> <button onClick={this.increment}>+1</button> </div> </> ) } } render(<Counter initialCount={42} />) 42
  • 9. 42 -1 +1 class CounterContainer extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { return ( <Counter count={this.state.count} increment={this.increment} decrement={this.decrement} handleChange={this.handleChange} /> ) } } function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<CounterContainer initialCount={42} />) 42
  • 10. 42 -1 +1 class CounterContainer extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { // NOTE: this is bad return Counter({ count: this.state.count, increment: this.increment, decrement: this.decrement, handleChange: this.handleChange, }) } } function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<CounterContainer initialCount={42} />) 42
  • 12. 42 -1 +1 class CounterContainer extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { return ( <Counter count={this.state.count} increment={this.increment} decrement={this.decrement} handleChange={this.handleChange} /> ) } } function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<CounterContainer initialCount={42} />) 42
  • 13. 42 -1 +1 const withCounter = Enhanced => class CounterContainer extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { return ( <Enhanced count={this.state.count} increment={this.increment} decrement={this.decrement} handleChange={this.handleChange} /> ) } } Counter = withCounter(Counter) function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<Counter initialCount={42} />) 42
  • 14. 42 -1 +1 const enhance = compose( withState('count', 'setCount', ({ initialCount }) => initialCount), withHandlers({ increment: ({ setCount }) => () => setCount(count => count + 1), decrement: ({ setCount }) => () => setCount(count => count - 1), handleChange: ({ setCount }) => event => setCount(parseInt(event.target.value)), }), pure, ) Counter = enhance(Counter) function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<Counter initialCount={42} />) 42
  • 16. Hello world! function Parent() { return ( <Wrapper> <Child who="world" /> </Wrapper> ) } function Child({ who }) { return `Hello ${who}!` } function Wrapper({ children }) { return <h1>{children}</h1> } render(Parent)
  • 17. Hello WORLD! function Parent() { return <Wrapper who="world" Component={Child} /> } function Child({ who }) { return `Hello ${who}!` } function Wrapper({ who, Component }) { const shoutedWho = who.toUpperCase() return ( <h1> <Component who={shoutedWho} /> </h1> ) } render(Parent)
  • 18. Hello WORLDz! function Parent() { return <Wrapper who="world" render={who => <Child who={who + 'z'} />} /> } function Child({ who }) { return `Hello ${who}!` } function Wrapper({ who, render }) { const shoutedWho = who.toUpperCase() return <h1>{render(shoutedWho)}</h1> } render(Parent)
  • 19. Hello WORLDz! function Parent() { return <Wrapper who="world">{who => <Child who={who + 'z'} />}</Wrapper> } function Child({ who }) { return `Hello ${who}!` } function Wrapper({ children, who }) { const shoutedWho = who.toUpperCase() return <h1>{children(shoutedWho)}</h1> } render(Parent)
  • 20. 42 -1 +1 class CounterContainer extends PureComponent { state = { count: this.props.initialCount } increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) render() { return ( <Counter count={this.state.count} increment={this.increment} decrement={this.decrement} handleChange={this.handleChange} /> ) } } function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<CounterContainer initialCount={42} />) 42
  • 21. 42 -1 +1 class CounterContainer extends PureComponent { increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) state = { count: this.props.initialCount, increment: this.increment, decrement: this.decrement, handleChange: this.handleChange, } render() { return this.props.children(this.state) } } function Counter({ initialCount }) { return ( <CounterContainer initialCount={initialCount}> {({ count, increment, decrement, handleChange }) => ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> )} </CounterContainer> ) } render(<Counter initialCount={42} />) 42
  • 22. 42 -1 +1 class CounterContainer extends PureComponent { increment = () => this.setState(({ count }) => ({ count: count + 1 })) decrement = () => this.setState(({ count }) => ({ count: count - 1 })) setCount = count => this.setState({ count }) handleChange = event => this.setCount(parseInt(event.target.value)) state = { count: this.props.initialCount, increment: this.increment, decrement: this.decrement, handleChange: this.handleChange, } render() { return this.props.children(this.state) } } class Counter extends PureComponent { renderCounter = ({ count, increment, decrement, handleChange }) => ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) render() { return ( <CounterContainer initialCount={this.props.initialCount}> {this.renderCounter} </CounterContainer> ) } } render(<Counter initialCount={42} />) 42
  • 24. They separate stateful logic They are composable functions They allow us to go fully functional They keep our component hierarchy flat
  • 25. 42 -1 +1 const enhance = compose( withState('count', 'setCount', ({ initialCount }) => initialCount), withHandlers({ increment: ({ setCount }) => () => setCount(count => count + 1), decrement: ({ setCount }) => () => setCount(count => count - 1), handleChange: ({ setCount }) => event => setCount(parseInt(event.target.value)), }), pure, ) Counter = enhance(Counter) function Counter({ count, increment, decrement, handleChange }) { return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<Counter initialCount={42} />) 42
  • 26. 42 -1 +1 function useCounter(initialCount) { const [count, setCount] = useState(initialCount) const increment = () => setCount(count + 1) const decrement = () => setCount(count - 1) const handleChange = event => setCount(parseInt(event.target.value)) return { count, increment, decrement, handleChange } } Counter = memo(Counter) function Counter({ initialCount }) { const { count, increment, decrement, handleChange } = useCounter(initialCount) return ( <> <h1>{count}</h1> <div className="input-group"> <button onClick={decrement}>-1</button> <input type="number" value={count} onChange={handleChange} /> <button onClick={increment}>+1</button> </div> </> ) } render(<Counter initialCount={42} />) 42
  • 31. Hello WORLD! function Hello({ who }) { return <h1>{`Hello ${who}!`}</h1> } const enhance = Enhanced => ({ who, ...props }) => { if (!who.length) return 'Name too short' const shoutedWho = who.toUpperCase() return <Enhanced {...props} who={shoutedWho} /> } Hello = enhance(Hello) render(<Hello who="world" />)
  • 32. world though ly tual issey const SimpleList = ({ whos }) => ( <ul style={styles.list}> {whos.map(who => ( <li>{who}</li> ))} </ul> ) const ComplexList = ({ renderEven, renderOdd, whos }) => ( <ul style={styles.list}> {whos.map((who, index) => (index % 2 ? renderEven(who) : renderOdd(who)))} </ul> ) const Parent = ({ whos, simple }) => { if (simple) return <SimpleList whos={whos} /> return ( <ComplexList renderEven={who => <li style={styles.even}>{'Even ' + who}</li>} renderOdd={who => <li style={styles.odd}>{'Odd ' + who}</li>} whos={whos} /> ) } render( <Parent whos={['world', 'though', 'ly', 'tual', 'issey']} simple={true} />, ) const styles = { list: { textAlign: 'left' }, odd: { color: 'grey' }, even: { color: 'cornflowerblue' }, }
  • 33. Which to use? Classes: getSnapshotBeforeUpdate / componentDidCatch HOCs: proxy / before render (change props, prevent renders) Render Props: mix logic / multiple sub-items / switch behavior Hooks: everything else