The road to react hooks
Younes
CopenhagenJS <november>
How do I share (stateful) code between
components ?
Code/Logic share
● Mixins
● Higher order components
● Render props (aka function as child)
● Children composition
Mixins
mixin
const windowResize = {
getInitialState() {
return { width: 0 };
},
handler() {
const {innerWidth: width} = window;
this.setState({ width });
},
componentDidMount() {
window.addEventListener("resize", this.handler);
},
componentWillUnmount() {
window.removeEventListener("resize", this.handler);
}
};
component
const Component = React.createClass({
mixins: [windowResize],
render() {
return this.state.width > 500
? this.props.children
: null;
}
});
Why not mixins ?
● implicit and hard to track dependencies (Indirection)
● name clashes
● snowballing complexity (DEFINE_ONCE, OVERRIDE_BASE ...)
● New ES-6 classes API
higher order components
a function that takes an existing component and
returns another component that wraps it
function withDataLoader(Component) {
return class extends React.Component {
state = { data: null, loading: true }
async componentDidMount() {
const data = await API.get(this.props.url);
this.setState({ data, loading: false });
}
render() {
return <Component data={this.state.data} loading={this.state.loading} />}
}
}
const EnhancedComponent = withDataLoader(MyComponent);
<EnhancedComponent url={url} />
Examples of HOC
● graphql(gql`{ ... }`)(Component);
● connect(mapState, actions)(Component);
● withRouter(Component);
Render props
share code using a prop whose value is a function
class DataLoader extends React.Component {
state = { data: null, loading: true };
async componentDidMount() {
// some caching logic maybe?
const data = await API.get(this.props.url);
this.setState({ data, loading: false });
}
render() {
const { loading, data } = this.state;
return this.props.render({ loading, data });
}
}
<DataLoader
url={url}
render={({ data, loading }) =>
/* some JSX */
}
/>
Examples of Render Props
● Apollo Query/Mutation
● React Router
● react-motion/spring
But!
● a lot of ceremony (HOCs more)
● Wrapper Hell
● classes?
Class Components/Classes ?
Class components ?
● Huge components, hard to reuse logic
● Classes are difficult for humans
● Classes are difficult for machines (transpile, hot reload, Tree
shaking)
Hooks ?
Functions that let you “hook into” React state and lifecycle features from function
components. Hooks don’t work inside classes — they let you use React without
classes
It's a proposal ?
Show me some code !
React relies on the order in which Hooks are called
Persisted Effectsfunction Component() {
const [name, setName] = useState('default');
useEffect(function effect1() { /*code*/ });
const [count, setCounter] = useState(0);
useEffect(function effect2() { /*code*/ });
// return some JSX
}
Current value: “default”
Current Function: ƒ effect1
Current value: 0
Current Function: ƒ effect2
Hooks Rules
● Only call Hooks at the top level (no loops, condition etc..)
● Only from React function components (expect custom Hooks)
● Names should start with use (linting hint)
useContext
function ComponentA() {
const theme = React.useContext(ThemeContext);
// other hooks
// return some jsx
}
useRef
function Focus() {
const inputRef = useRef(null);
const clickHandler = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={clickHandler}>Focus the input</button>
</>
);
}
useReducer
function reducer(state, action) {
switch (action) {
case 'increment': return state.count + 1;
case 'decrement': return state.count - 1;
default: return state;
}
}
function Counter({ initialCount }) {
const [state, dispatch] = useReducer(reducer, initialCount);
return (
<>
Count: {state.count}
<button onClick={() => dispatch('increment')}>+</button>
<button onClick={() => dispatch('decrement')}>-</button>
</>
);
}
Custom hooks
function FriendStatus({ friendId }) {
const { match, history } = useRouter();
const state = useConnect(mapState);
const { data, loading } = Apollo.useQuery(QUERY);
// return some jsx
}
Custom hooks by community
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID,
handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID,
handleStatusChange);
};
});
return isOnline;
}
function FriendStatus({ friendId }) {
const isOnline = useFriendStatus(friendId);
return isOnline ? 'Online' : 'Offline';
}
Custom hooks
React.PureComponent ?
React.memo HOC (aka Pure)
import React from "react";
const MyComponent = ({ props1, props2 }) => {
// return some JSX
};
export default React.memo(MyComponent, customEqual?);
This is all ?
Nop, Suspense
(async/concurrent React)
1. before render() method, try to read a value from the cache.
2. If the value is already in the cache, the render continues
3. If the value is not already in the cache, throws the promise as an error
4. When the promise resolves, React continues from where it stopped
Suspense
Suspense
....
<ErrorBoundry>
<Suspense fallback={Loader}>
<ComponentA>
<AsyncDataComponent />
</ComponentA>
</Suspense>
</ErrorBoundry>
....
● Class components are not dead
● But there is a lot of benefits of using Hooks
● A beautiful future is ahead (concurrent mode, suspense)
Thank you!
_younesmln

Road to react hooks

  • 1.
    The road toreact hooks Younes CopenhagenJS <november>
  • 2.
    How do Ishare (stateful) code between components ?
  • 3.
    Code/Logic share ● Mixins ●Higher order components ● Render props (aka function as child) ● Children composition
  • 4.
  • 5.
    mixin const windowResize ={ getInitialState() { return { width: 0 }; }, handler() { const {innerWidth: width} = window; this.setState({ width }); }, componentDidMount() { window.addEventListener("resize", this.handler); }, componentWillUnmount() { window.removeEventListener("resize", this.handler); } }; component const Component = React.createClass({ mixins: [windowResize], render() { return this.state.width > 500 ? this.props.children : null; } });
  • 7.
    Why not mixins? ● implicit and hard to track dependencies (Indirection) ● name clashes ● snowballing complexity (DEFINE_ONCE, OVERRIDE_BASE ...) ● New ES-6 classes API
  • 9.
    higher order components afunction that takes an existing component and returns another component that wraps it
  • 10.
    function withDataLoader(Component) { returnclass extends React.Component { state = { data: null, loading: true } async componentDidMount() { const data = await API.get(this.props.url); this.setState({ data, loading: false }); } render() { return <Component data={this.state.data} loading={this.state.loading} />} } } const EnhancedComponent = withDataLoader(MyComponent); <EnhancedComponent url={url} />
  • 11.
    Examples of HOC ●graphql(gql`{ ... }`)(Component); ● connect(mapState, actions)(Component); ● withRouter(Component);
  • 13.
    Render props share codeusing a prop whose value is a function
  • 14.
    class DataLoader extendsReact.Component { state = { data: null, loading: true }; async componentDidMount() { // some caching logic maybe? const data = await API.get(this.props.url); this.setState({ data, loading: false }); } render() { const { loading, data } = this.state; return this.props.render({ loading, data }); } } <DataLoader url={url} render={({ data, loading }) => /* some JSX */ } />
  • 15.
    Examples of RenderProps ● Apollo Query/Mutation ● React Router ● react-motion/spring
  • 16.
    But! ● a lotof ceremony (HOCs more) ● Wrapper Hell ● classes?
  • 17.
  • 18.
    Class components ? ●Huge components, hard to reuse logic ● Classes are difficult for humans ● Classes are difficult for machines (transpile, hot reload, Tree shaking)
  • 19.
    Hooks ? Functions thatlet you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes
  • 20.
  • 21.
  • 22.
    React relies onthe order in which Hooks are called Persisted Effectsfunction Component() { const [name, setName] = useState('default'); useEffect(function effect1() { /*code*/ }); const [count, setCounter] = useState(0); useEffect(function effect2() { /*code*/ }); // return some JSX } Current value: “default” Current Function: ƒ effect1 Current value: 0 Current Function: ƒ effect2
  • 23.
    Hooks Rules ● Onlycall Hooks at the top level (no loops, condition etc..) ● Only from React function components (expect custom Hooks) ● Names should start with use (linting hint)
  • 24.
    useContext function ComponentA() { consttheme = React.useContext(ThemeContext); // other hooks // return some jsx }
  • 25.
    useRef function Focus() { constinputRef = useRef(null); const clickHandler = () => { inputRef.current.focus(); }; return ( <> <input ref={inputRef} type="text" /> <button onClick={clickHandler}>Focus the input</button> </> ); }
  • 26.
    useReducer function reducer(state, action){ switch (action) { case 'increment': return state.count + 1; case 'decrement': return state.count - 1; default: return state; } } function Counter({ initialCount }) { const [state, dispatch] = useReducer(reducer, initialCount); return ( <> Count: {state.count} <button onClick={() => dispatch('increment')}>+</button> <button onClick={() => dispatch('decrement')}>-</button> </> ); }
  • 27.
  • 28.
    function FriendStatus({ friendId}) { const { match, history } = useRouter(); const state = useConnect(mapState); const { data, loading } = Apollo.useQuery(QUERY); // return some jsx } Custom hooks by community
  • 29.
    function useFriendStatus(friendID) { const[isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; } function FriendStatus({ friendId }) { const isOnline = useFriendStatus(friendId); return isOnline ? 'Online' : 'Offline'; } Custom hooks
  • 30.
  • 31.
    React.memo HOC (akaPure) import React from "react"; const MyComponent = ({ props1, props2 }) => { // return some JSX }; export default React.memo(MyComponent, customEqual?);
  • 32.
    This is all? Nop, Suspense (async/concurrent React)
  • 34.
    1. before render()method, try to read a value from the cache. 2. If the value is already in the cache, the render continues 3. If the value is not already in the cache, throws the promise as an error 4. When the promise resolves, React continues from where it stopped Suspense
  • 35.
  • 36.
    ● Class componentsare not dead ● But there is a lot of benefits of using Hooks ● A beautiful future is ahead (concurrent mode, suspense)
  • 37.