React

Learning Hooks + Redux

staffordwilliams.com

@staff0rd

  • How did I get here?
  • Learning Hooks
  • Learning Redux

How did I get here?

2006

2007

2009

2010

2012

2013

2015

  • Quit my job
  • Learnt AngularJS

2016

2018

Components & Props


						function Welcome(props) {
						  return <h1>Hello, {props.name}</h1>;
						}
					

            function Welcome(props) {
              return <h1>Hello, {props.name}</h1>;
            }
            
            function App() {
              return (
                <div>
                  <Welcome name="Sara" />
                  <Welcome name="Cahal" />
                  <Welcome name="Edite" />
                </div>
              );
            }
            

State & Lifecycle


              function Welcome(props) {
                return <h1>Hello, {props.name}</h1>;
              }
            

              class Welcome extends React.Component {
                render() {
                  return <h1>Hello, {this.props.name}</h1>;
                }
              }
            

              class Welcome extends React.Component {
                constructor(props) {
                  super(props);
                  this.state = {
                    name: "Meligy",
                  };
                }
              
                render() {
                  return <h1>Hello, {this.state.name}</h1>;
                }
              }
            

              class Welcome extends React.Component {
                constructor(props) {
                  super(props);
                  this.state = {
                    name: "Meligy",
                  };
                }

                componentDidMount() {}
                componentWillUnmount() {}
              
                render() {
                  return <h1>Hello, {this.state.name}</h1>;
                }
              }
            

              class Welcome extends React.Component {
                componentDidMount() {
                  something.subscribe("newPerson", (person) => {
                    this.setState({
                      name: person.name
                    });
                  });
                }
              }
            

              class Welcome extends React.Component {
                componentDidMount() {
                  something.subscribe("newPerson", (person) => {
                    this.setState({
                      name: person.name
                    });
                  });
                }

                componentWillUnmount() {
                  something.unsubscribe("newPerson");
                }
              }
            

              class Welcome extends React.Component {
                constructor(props) { ... }

                componentDidMount() { ... }

                componentWillUnmount() { ... }

                render() { ... }
              }
            

2019

Hooks

Hooks

  • useState
  • useEffect
  • Rules
  • Custom hooks

              class Welcome extends React.Component {
                constructor(props) {
                  super(props);
                  this.state = {
                    name: "Meligy",
                  };
                }
              
                render() {
                  return <h1>Hello, {this.state.name}</h1>;
                }
              }
            

              function Welcome() {
                const [name, setName] = useState("Meligy");

                return <h1>Hello, {name}</h1>;
              }
            

              class Welcome extends React.Component {
                ...
                componentDidMount() {
                  something.subscribe("newPerson", (person) => {
                    this.setState({
                      name: person.name
                    });
                  });
                }

                componentWillUnmount() {
                  something.unsubscribe("newPerson");
                }
                ...
              }
            

              function Welcome() {
                const [name, setName] = useState("Meligy");

                useEffect(() => {
                  something.subscribe("newPerson", (person) => {
                    setName(person.name);
                  });
                  return () => something.unsubscribe("newPerson");
                }, []);

                return <h1>Hello, {name}</h1>;
              }
            

              componentDidMount() {
                document.title = `You have ${this.state.beers} left`;
              }
            
              componentDidUpdate() {
                document.title = `You have ${this.state.beers} left`;
              }
            

              componentDidMount() {
                document.title = `You have ${this.state.beers} left`;
              }
            
              componentDidUpdate(prevProps, prevState) {
                // optimised!
                if (prevState.beers != this.state.beers) {
                  document.title = `You have ${this.state.beers} left`;
                }
              }
            

              useEffect(() => {
                document.title = `You have ${beers} left`;
              }, [beers]);
            

Rules

  • Only Call Hooks at the Top Level
    
                      // NO!
                      if (condition) {
                        useEffect(() => { ... });
                      }
                      // YES!
                      useEffect(() => {
                        if (condition) { ... }
                      });
                    
  • Call Hooks from React function components
  • Call Hooks from custom Hooks
  • eslint-plugin-react-hooks

Custom hooks


              class ShowTheLocation extends React.Component {
                render() {
                  return (
                    <Link onClick={() => this.props.history.push("/")}>
                      You are now at {this.props.location.pathname}
                    </Link>
                  );
                }
              } 
              const ShowTheLocationWithRouter =
                withRouter(ShowTheLocation);
            

              function ShowTheLocation() {
                const history = useHistory();
                const location = useLocation();

                return (
                  <Link onClick={() => history.push("/")}>
                    You are now at {location.pathname}
                  </Link>
                );                
              } 
            

Also in 2019...

Redux 101

  • Single source of truth
    Store
  • State is read-only
    Trigger modification by dispatching Actions
  • Change via pure function
    Reducers take current state and an action to produce new state

Redux

  • Store
  • Action
  • Reducer

              console.log(store.getState());
              // prints { beers: 10 }

              store.dispatch({ type: 'DRINK_BEER' });

              function reducer(state = { beers: 10 }, action) {
                switch(action.type) {
                  case 'DRINK_BEER': return ({
                    ...state,
                    beers: state.beers - 1,
                  });
                }
              };
            

npm install redux --save

  • redux
  • react-redux
  • @reduxjs/toolkit
  • redux-devtools

react-redux

  • Provider
  • connect

Provider


              return (
                <Provider store={store}>
                  <App />
                </Provider>
              );
            

connect


              const mapStateToProps = (state) => {
                return {
                  beers: state.beers,
                  beersDrunk: 24 - state.beers,
                }
              }
              
              const mapDispatchToProps = dispatch => {
                return {
                  drinkBeer: () => dispatch({ type: 'DRINK_BEER' }),
                }
              }
              
              export default connect(
                mapStateToProps,
                mapDispatchToProps
              )(MyComponent)
            

Selectors


              const mapStateToProps = (state) => {
                return {
                  beers: state.beers,
                  beersDrunk: 24 - state.beers,
                }
              }
            

Selectors


              const mapStateToProps = createSelector(
                beersSelector,
                beersDrunkSelector,
                (beers, beersDrunk) => ({
                  beers,
                  beersDrunk,
              });
            

Action creators


              const mapDispatchToProps = dispatch => {
                return {
                  drinkBeer: () => dispatch({ type: 'DRINK_BEER' }),
                }
              }
            

Action creators


              const {
                drinkBeer,
              } = createActions(
                'DRINK_BEER'
              );

              export default {
                drinkBeer,
              }
            

Action creators


              const mapDispatchToProps = dispatch => 
                bindActionCreators(actions, dispatch);
            

connect


              const mapStateToProps = createSelector(
                beersSelector,
                beersDrunkSelector,
                (beers, beersDrunk) => ({
                  beers,
                  beersDrunk,
              });
              
              const mapDispatchToProps = dispatch => 
                bindActionCreators({ actions }, dispatch);
              
              export default connect(
                mapStateToProps,
                mapDispatchToProps
              )(MyComponent)
            

Redux Toolkit

  • configureStore
    • combineReducers, middleware
  • createReducer
    • switch, immer
  • createAction
  • createSelector
  • createSlice
  • createAsyncThunk
  • createEntityAdapter

redux hooks

  • useSelector
  • useDispatch

              function MyComponent() {
                const beers = useSelector(state => state.beers);
                const dispatch = useDispatch();

                return (
                  <Button onclick={() => dispatch(actions.drinkBeer())}>
                    Drink one of the {beers} beers.
                  </Button>
                );
              }
            

staffordwilliams.com

@staff0rd