React: Note on what happens during rendering and re-rendering

Rendering has 2 phases:
Initial Render:
      Render phase:
       -start at root
       -goes down leaf components
       -use createElement() to convert jsx to react elements

      Commit phase:
       -all react elements are handed to commit phase and are applied to the DOM

Rerender:
      Render phase:
       -search all components that have been flagged as needing updates (useState setState method, useReducer dispatch method)
       -convert flagged components to react elements
       -compare previous virtual DOM to new virtual DOM
       -if components convert to same element as previous then discarded

      Commit phase:
       -all the changed elements are handed to commit phase and are applied to the DOM

useState
* The setter function from a useState hook will cause the component to re-render. * The exception is when update a State Hook to the same value as the current state.
      -Same value after initial render? component will not re-render.
      - Same value after re-render? React will render that specific component one more time but will be discarded before going to commit phase

useReducer
* The dispatch function from a useReducer hook will cause the component to re-render.
* The exception is when update a State Hook to the same value as the current state.
      -Same value after initial render? component will not re-render.
      -Same value after re-render? React will render that specific component one more time but will be discarded before going to commit phase

State Immutability
* Mutating an object or an array as state will not cause a re-render when used with the useState or useReducer hook
* To re-render, make a copy of the existing state, modify as necessary and then pass the new state to the setter function or while returning from a reducer function

Parent & Child
* When parent component re-renders, all child components re-render
* New state same as old state after initial render? Both parent and child do not re-render
* New state same as old state after re-render? Parent re-renders one more time but child never re-renders
* Child components re-rendering but not affecting the DOM is “unnecessary render”

Same element reference
-To avoid “unnecessary renders”,
* extract the child component and pass it down as a prop of the parent component (as children element)
* Whenever there is a re-render caused by a change in the state of the parent component, React will optimize the re-render for you by knowing that the props has to be referencing the same element before and after the render

React Memo
* In React, when a parent component renders ,a child component might unnecessarily render.
* To optimize this behavior, you can use React.memo and pass in the child component.
* React.memo will perform a shallow comparison of the previous and new props and re-render the child component only if the props have changed.

Incorrect React Memo
-React.Memo in child component for all cases
-when parent component re-renders for all cases
 ・Parent Child
     - If the child component has children elements as props, child component will re-render even with React.Memo because children elements are always a new reference
 ・Impure component
     - Even if the child component has a variable that can change (date, random, etc) the child component will not re-render because of React.Memo
 ・Prop reference
     -If child component has objects or functions as props from parent component, child component will always re-render even with React.Memo because every time the parent component re-renders, new objects and functions are created.

React: Simple context template

import React, { createContext, Dispatch, useContext, useReducer } from "react"

const hogeData = {};

export const HogeContext = createContext<any>(hogeData)
export const HogeDispatchContext = createContext<Dispatch<any>>(() => { })

export const HogeContextProvider: React.FC = ({ children }) => {
  const [state, dispatch] = useReducer((_: any, action: any) => {
    return action;
  }, hogeData)

  return (
    <HogeContext.Provider value={state}>
      <HogeDispatchContext.Provider value={dispatch}>
        {children}
      </HogeDispatchContext.Provider>
    </HogeContext.Provider>
  )
}

Javascript: Skip an element array and make a new array

var newArray = array.reduce(function(result, hoge) {
  if (some condition) {
    result.push(hoge);
  }
  return result;
}, []);

or

var newArray = array.filter(function(hoge) {
  if (some condition) {
    return hoge;
  }
});

React: Outside Click Detection

import React, { useRef, useEffect } from "react";

export default SomeFunctionComponent(props) {
  const elementRef = useRef(null);

  useEffect(() => {
    function handleOutsideClick(event) {
      if (ref.current && !ref.current.contains(event.target)) {
        do something
      }
    }
    document.addEventListener("mousedown", handleOutsideClick);
    return () => {
      // Unbind the event listener on clean up
      document.removeEventListener("mousedown", handleOutsideClick);
    };
  }, [elementRef]);
}

  return (
    <div ref={elementRef}>{props.children}</div>;
  )
}

React Context API examples

*context api example for me, myself, and i

2 ways to use context api with inital value

hoge.contex.jsx

import { createContext } from ‘react’
import HOGE_DATA from ‘./hoge.data.js’ // some data with values

const HogeContext = createContext(HOGE_DATA)

export default HogeContext

1 using Context.Consumer

foo.jsx

import HogeContext from ‘hoge.context.jsx’

<HogeContext.Consumer>
{
    data => {
        const  hogeData = data[hoge]
        const { foo, blah } = hoge
        return (
            use the destructure data as arguments
        )

    }
}
</HogeContext.Consumer>

2 useContext

foo.jsx

import { useContedt } from ‘react’


const data = useContext(HogeContext)
const hogeData = data[hoge]
const { foo, blah } = hogeData

return (
    <div className=‘foo’>{foo}/>
    <div className=‘blah’>{blah}/>
)

To dynamically set values to context

hoge.context.jsx

import { createContext } 

const HogeContext = createContext(undefined)

export default HogeContext
foo.jsx

import { useContext } from ‘react’
import { HogeContext } from ‘hoge.context’

const foo = {
    bool: true,
    method : () => concsole.log(“blah”);
}

<HogeContext.Provide value={foo}r>
    <Component />
<HogeContext.Provider>
component.jsx

import { useContext } from ‘react’
import HogeContext from ‘hoge.context’

const { bool, method } = useContext(HogeContext)

but this is bad practice so....

make context with initial values and pass local values

hoge.context.jsx

import { createContext } 

const HogeContext = createContext({
    bool: true,
    method: () => {}
})

export default HogeContext
foo.jsx

import { useState, useContext } from ‘react’
import { HogeContext } from ‘hoge.context’

const [bool, setBool] = useState(true)
const method = () => setBool(!bool)

<HogeContext.Provide value={{
    bool,
    method,
}}r>
    <Component />
<HogeContext.Provider>
component.jsx

import { useContext } from ‘react’
import HogeContext from ‘hoge.context’

const { bool, method } = useContext(HogeContext)

or create a provider

hoge.provider.jsx

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

import {
    addItemToHoge,
    removeItemFromHoge,
    HogeItem,
    totalHogeItem,
    totalHogePrice,
} from ‘./hoge.utils.js';

export const HogeContext = createContext({
    hidden: true,
    toggleHidden: () => {},
    hogeItems: [],
    addItem: () => {},
    removeItem: () => {},
    clearItemFromHoge: () => {},
    hogeItemsCount: 0,
    totalPrice: 0,
});

const HogeProvider = ({ children }) => {
    const [hidden, setHidden] = useState(true);
    const [hogeItems, setHogeItems] = useState([]);
    const [hogeItemsCount, setHogeItemsCount] = useState(0);
    const [totalPrice, setTotalPrice] = useState(0);

    const addItem = (item) => setHogeItems(addItemToHoget(hogeItems, item));
    const removeItem = (item) =>
        setHogeItems(removeItemFromHoge(hogeItems, item));
    const clearItemFromHoge = (item) => setHogeItems(clearItem(hogeItems, item));

    const toggleHidden = () => setHidden(!hidden);

    useEffect(() => {
        setHogeItemsCount(totalHogeItem(hogeItems));
        setTotalPrice(totalHogePrice(hogeItems));
    }, [hogeItems]);

    return (
        <HogeContext.Provider
            value={{
                hidden,
                addItem,
                toggleHidden,
                hogeItems,
                removeItem,
                clearItemFromHoge,
                hogeItemsCount,
                totalPrice,
            }}
        >
            {children}
        </HogeContext.Provider>
    );
};

export default HogeProvider
index.js

import HogePorvider from ‘’hoge.provider’

<HogeProvider>
    <App/>
<HogeProvider>