Hooks in React

What is React?

Developers 💜 React

Developers 🤬 React

History of React

July 2013 ─ v0.3

First public release

const ToggleButton = React.createClass({
  name: "ToggleButton",
  getInitialState() {
    return { toggle: false };
  },
  handleClick() {
    this.setState({
      toggle: !this.state.toggle,
    });
  },
  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.toggle ? "On" : "Off"}
      </button>
    );
  },
});

Mixins

const ToggleMixin = {
  getInitialState() {
    return { toggle: false };
  },
  handleClick() {
    this.setState({
      toggle: !this.state.toggle,
    });
  },
};

Mixins

React.createClass({
  name: "ToggleButton",
  mixins: [ToggleMixin],
  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.toggle ? "On" : "Off"}
      </button>
    );
  },
});

History of React

December 2013 ─ v0.8

Published on npm

History of React

Oct 2015 ─ v0.14

Separated into react and react-dom packages

Stateless Functional Components

const MyButton = ({ onClick, toggled }) => (
  <button onClick={onClick}>{toggled ? "On" : "Off"}</button>
);

History of React

Apr 2016 ─ v15

First "stable" release

const withToggle = InnerComponent =>
  class extends React.Component {
    state = { toggle: false };

    handleToggle() {
      this.setState({ toggle: !this.state.toggle });
    }

    render() {
      return (
        <InnerComponent
          {...this.props}
          toggled={this.state.toggle}
          onClick={() => this.handleToggle()}
        />
      );
    }
  };

SFCs + HoCs

const MyButton = ({ onClick, toggled }) => (
  <button onClick={onClick}>{toggled ? "On" : "Off"}</button>
);

const ToggleButton = withToggle(MyButton);
const ToggleButtonWithPopover = withPopover(withToggle(MyButton));
const IsThisEvenReallyAButton = _.flowRight(
  withPopover,
  withToggle,
  withCounter,
  withSomethingFromMyReduxStore,
  withCatPhotosFromApi
)(MyButton);
<Popover>
  <Toggle>
    <Counter>
      <CatPhotoApi>
        <MyButton />
      </CatPhotoApi>
    </Counter>
  </Toggle>
</Popover>

Render Props

a.k.a children as a function

Render Props

const MyButton = () => (
  <ToggleBehaviour>
    {({ toggled, toggle }) => (
      <button onClick={toggle}>{toggled ? "On" : "Off"}</button>
    )}
  </ToggleBehaviour>
);

Enter: Hooks

Stateless Functional Components

useState

const useToggle = (initial = false) => {
  const [toggled, setToggled] = useState(initial);
  return [toggled, () => setToggled(!toggled)];
};
const useCounter = (initial = 0) => {
  const [counter, setCounter] = useState(initial);
  return [counter, () => setCounter(counter + 1)];
};

useEffect

const MyOverpoweredComponent = () => {
  const { x, y } = useWindowScrollPosition();
  useDocumentTitle(`Scrolled to (${x}, ${y}`);

  return (
    <div>
      {x}, {y}
    </div>
  );
};

useEffect

const useDocumentTitle = title =>
  useEffect(() => {
    let originalTitle = document.title;
    document.title = title;

    return () => {
      document.title = originalTitle;
    };
  }, [title]);

useContext

const ThemedButton = () => {
  const theme = useContext(MyThemeContext);
  return <button style={theme}>Button</button>;
};

useReducer

const [state, dispatch] = useReducer(reducer, initialArg);

useReducer + useContext

(+ about 10 lines of code)

==

redux + react-redux

useRef

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Other built-in hooks

  • useMemo / useCallback to avoid re-executing expensive functions
  • useLayoutEffect - blocking version of useEffect
  • useImperativeHandle to expose imperative code to parents

Hooks allow you to...

Organise code by behaviour, not by lifecycle method

Hooks allow you to...

Compose behaviours and re-use them.

Hooks allow you to...

Use well-tested 3rd party modules to add complex behaviours without:

  • sacrificing control of rendering
  • introducing performance overheads

Hooks allow you to...

Never write another class in React again.

Apollo (via react-apollo-hooks)

// Query
const { data, error, loading } = useQuery(GET_CATS);
// Mutation
const toggleLikedCat = useMutation(TOGGLE_LIKED_CAT, {
  variables: { id: catId },
});

Apollo (via react-apollo-hooks)

// Subscription
const { data, error, loading } = useSubscription(CAT_LIKE_COUNT_CHANGED, {
  variables: { id: catId },
});

Redux (via react-redux@next)

// select data from store
const cat = useSelector(state => state.cats[catId]);
// dispatch actions to store
const dispatch = useDispatch();
const toggleLikedCat = dispatch({ type: CAT_LIKE_ACTION, catId });

etc.

const [value, setValue] = useLocalStorage();
const { width, height, top, left } = useClientBoundingBox(elementRef);
const { x, y } = useMousePosition(); // useScrollPosition()
const { data, isLoading, error } = useFetch(CAT_API_URL);
useOnClickOutside(elementRef, closeModal);