Hooks in Practice

Using new React feature Hooks

react

01-April-2020

The last month React team has been released new update in react bringing the new feature of hooks, if you’re looking for definition of hooks head to react website and check An intro to hooks out.

If you’re like me doesn’t like to talk too much and taking directly to the best practises of using any of the new features in technology, let’s talk than.

I will explain in both javascript and typescript, maybe for javascript sometimes you just need to remove types from typescript file, and sure change file extension name to js

1.useState

useState is alternative for this.state in class component, it returns value and it’s action, let say that we have counter component will return counter number:

import React, {Component} from "react"

class Counter extends Component {
  state= {
    count: 0
  }
render(){
  const {count} = this.state
  return (
    <div>
      <h2>Counter: {count}</h2>
      <button onClick={()=> this.setState({count: count+1 })}>+</button>
    <div>
    )
 }
}

export default Counter

Now with useState will be like this:

import React, {useState} from 'react'

const Counter = (props)=> {
  const [count, setCount]= useState(0);
  return (
   <div>
    <h2>Counter: {count}</h2>
    <button onClick={()=> setCount(count+1)}>+</button>
    <div>
  )
}

export default Counter

Nice.right? if you would like to add types will be:

import React, {useState, FC } from 'react';

interface iProps {}

const Counter:FC<iProps> = (props) => {
  const [count, setCount]= useState<number>(0)
  
  return (
  <div>
    <h2>Counter: {count}</h2>
    <button onClick={() => setCount(count+1)}>+</button>
  </div>
  )
}
export default Counter;

2.useEffect

This function is an alternative for life cycle function in class component, specifically componentDidMount and componentDidUpdate

import React, { useState, useEffect } from 'react'

function LifeCycleMethod(props){
 const [count, setCount]= useState(0)
 
 useEffect(()=> {
  let data= JSON.parse(localStorage.getItem('count'))
  
  setCount(data)
 }, [])

  useEffect(()=> {
  localStorage.setItem('count', JSON.stringfy(count))
 },[count])
  
  return(
    <div>
      <h3>Counter: {count}</h3>
      <button onClick={()=> setCount(count+1)}>+</button>
    </div>
}

With types you probably could add types from the declaration of your state:

const [count, setCount] = useState<number>(0)

Than if you want to assign another types to it will return false

setCount('count') // return error

There is a term where you could build your own custom hooks, which is actually ability to useState, and useEffect outside of your function.

This term called Custom Hooks, you’ll heard a lot about it in the upcoming days

example:

import React, { FC, useState, ChangeEvent, FormEvent } from 'react';

type E = ChangeEvent<HTMLInputElement> | FormEvent<HTMLInputElement>

interface Props {}

const NameFields: FC<Props> = props => {
  const name = useInputField('');
  const firstName = useInputField('');
  return (
    <div>
      <div>
        <label>Name: </label>
        <input {...name} />
      </div>
      <div>
        <label>First name : </label>
        <input {...firstName} />
      </div>
    </div>
  )
}
// custom hooks
const useInputField = (initialValue: string) => {
  const [value, setValue] = useState(initialValue)
  const handleChange = (e: E) => setValue(e.currentTarget.value)
  return { value, onChange: handleChange }
}

export default NameFields;

3.useRef

function CustomTextInput(props) {
 const textInput = useRef();
 
  useEffect(()=> {
    textInput.current.focus();
  })
     
 return (
      <div>
        <input
          type="text"
          ref={textInput}
         value='focused input'
        />
      </div>
    );
 }

With types this will becomes very handy to expect the output of you html element

import React from 'react';

interface Props {
  onChange(e: E): void;
  value: string;
  placeholder?: string | 'placeholder';
  type?: string | 'text';
}

const InputField: SFC<Props> = ({ onChange, value, placeholder, type }) => {
  const inputEl = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputEl.current.focus();
  })

  return (
    <input onChange={onChange} value={value} ref={inputEl} type={type} placeholder={placeholder} />
  )
}

export default InputField;

4.useContext

This hook used for Context API, if you don’t know what Context API is, it’s alternative for Redux came from react team to solve state problem and passing props to children and grandchildren. First of all, we will take a look how to use Context api in normal class Component:

import React, { Component, createContext } from 'react';

const NumberContext = createContext()

class NumberProvider extends Component {
 state = {
  number: 0
 }

render(){
  const { number } = this.state;
 return (
   <NumberContext.Provider value={number}>
     <NumberContent />
   </NumberContext.Provider>
   )
 }
}

class NumberContent extends Component {
 render(){
  return (
    <NumberContext.Consumer>
     {(value)=> 
       <div>
         {value}
       </div>}
    </NumberContext.Consumer>
  )
 }
}
export default NumberContent;
import React, { createContext, useContext } from 'react';

const NumberContext = createContext();

function NumberProvider(props){
 const [number, setNumber]= useState(0);

 return (
   <NumberContext.Provider value={number}>
     <NumberContent />
   </NumberContext.Provider>
   )
}

function NumberContent(props){
  const state = useContext(NumberContext); // this will return number
	
  return (
    <div>
	    <h3>{state}</h3>
    </div>
  )
}

With types I will use another example:

type Theme = 'light' | 'dark';

// create Context and taking out initial value 'dark'
const ThemeContext = createContext<Theme>('dark');

const App = () => (
  <ThemeContext.Provider value="dark">
    <MyComponent />
  </ThemeContext.Provider>

);

const MyComponent = () => {
  const theme = useContext(ThemeContext);
  return <div>The theme is {theme}</div>;
}

5.useReducer

import React, { useReducer } from 'react';
interface iState {
  count: number;
}
 
type Action = | { type: 'INC' } | { type: 'DEC' } | { type: 'RESET'};
 
const counterReducer = (state: iState, action: Action) => {
  switch (action.type) {
    case 'INC':
      return { count: state.count + 1 };
    case 'DEC':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    default:
      throw new Error();
  }
};
 
 
 
 
interface Props {}
 
 
const App:FC<Props> = (props) => {
 const [state, dispatch] = useReducer(counterReducer, { count: 0 });
     
 return(
    <div>
       <h2>{state.count}</h2>
       <button onClick={()=> dispatch({ type: "INC" })}>+</button>
       <button onClick={()=> dispatch({ type: "DEC" })}>-</button>
       <button onClick={()=> dispatch({ type: "RESET" })}>CLICK FOR RESET</button>
   </div>
}
 
export default App;

With that you could go far by using useContext and useReducer together.



Hopefully That was clear and helpful. Don't Forget to share.


Published on CodeReview by Mustafa Alroomi on 01-April-2020