The Journey of #100DaysOfCode (@sourabhbagrecha)

#Day68 of #100daysofcode

Today I learned about the Compound Component React pattern. Compound components are components that work together to form a complete UI. I am working on an example that uses this React Pattern and will share the implementation tomorrow.

2 Likes

#Day69 of #100daysofcode

Today I finished implementing a Compound Component, which provides a set of components that implicitly share state for a simple yet powerful declarative API for reusable components.

In the following example, we are sharing the on & toggle values across different components implicitly, that’s making our App component really extensible and declarative.

import * as React from 'react'
import {Switch} from '../switch'

function Toggle(props) {
  const [on, setOn] = React.useState(false)
  const toggle = () => setOn(!on)

  return React.Children.map(props.children, child => {
    return React.cloneElement(child, {
      on,
      toggle,
    })
  })
}

const ToggleOn = ({on, children}) => (on ? children : null)
const ToggleOff = ({on, children}) => (on ? null : children)
const ToggleButton = ({on, toggle}) => <Switch on={on} onClick={toggle} />

function App() {
  return (
    <div>
      <Toggle>
        <ToggleOn>The button is on</ToggleOn>
        <ToggleOff>The button is off</ToggleOff>
        <ToggleButton />
      </Toggle>
    </div>
  )
}

export default App
3 Likes

#Day70 of #100daysofcode

Today I learned about Flexible Compound Component React-Pattern, which is basically a level ahead of Compound Component in which it provides the ability to nest components at any level.
Now, these kind of requirements in React usually requires you to perform prop drilling to the complete depth just so that you can provide a value to the bottom-most component, but React Context comes pretty helpful in those cases, therefore, in the following example I will be using this React Pattern to share implicit state between deeply nested components as well using React’s Context API:

const ToggleContext = React.createContext()

function Toggle({children}) {
  const [on, setOn] = React.useState(false)
  const toggle = () => setOn(!on)

  return (
    <ToggleContext.Provider value={{on, toggle}}>
      {children}
    </ToggleContext.Provider>
  )
}

function ToggleOn({children}) {
  const {on} = React.useContext(ToggleContext)
  return on ? children : null
}

function ToggleOff({children}) {
  const {on} = React.useContext(ToggleContext)
  return on ? null : children
}

function ToggleButton({...props}) {
  const {on, toggle} = React.useContext(ToggleContext)
  return <Switch on={on} onClick={toggle} {...props} />
}

function App() {
  return (
    <div>
      <Toggle>
        <ToggleOn>The button is on</ToggleOn>
        <ToggleOff>The button is off</ToggleOff>
        <div>
          <ToggleButton />
        </div>
      </Toggle>
    </div>
  )
}

2 Likes

#Day71 of #100daysofcode

Today I added custom hook validation to my Flexible Compound Component to avoid any runtime errors that might get caused due to invalid usage of ToggleContext.
Here’s a snippet that demonstrates the implementation of the custom useToggle hook and how it gets consumed in the children component.

function useToggle() {
  const context = React.useContext(ToggleContext)
  if (!context) throw Error('useToggle must be consumed within <Toggle/>')
  return context
}

function ToggleOn({children}) {
  const {on} = useToggle()
  return on ? children : null
}
4 Likes

#Day72 of #100daysofcode

Today I learned about the Prop Collections and Getters Pattern, which allows our hook to support common use cases for UI elements people build with our hook.
This is especially useful when we want to make our UI components that ensures Accessibility standards.

function useToggle() {
  const [on, setOn] = React.useState(false)
  const toggle = () => setOn(!on)

  return {on, toggle, togglerProps: {'aria-pressed': on, onClick: toggle}}
}

function App() {
  const {on, togglerProps} = useToggle()
  return (
    <div>
      <Switch on={on} {...togglerProps} />
      <hr />
      <button aria-label="custom-button" {...togglerProps}>
        {on ? 'on' : 'off'}
      </button>
    </div>
  )
}
3 Likes

#Day73 of #100daysofcode

Today my best friend @anshul_bhardwaj has also joined us in this adventure of #100daysofcode.
He is on his way to become a full-stack developer in the next few months, wish him luck.

As of my updates for today, I learned about prop-getters.
Sometimes the users of our hook might want to implement their own functions while at the same time they want to leverage all the props that our hooks provide by default, therefore we need to provide an API that will provide them the ability to add custom features that they want to be executed combinedly with the defaults.

In the following example, we are providing the ability to execute a custom onClick function alongside the default toggle function.

function useToggle() {
  const [on, setOn] = React.useState(false)
  const toggle = () => setOn(!on)

  return {
    on,
    toggle,
    getTogglerProps: ({onClick, ...props} = {}) => {
      return {
        'aria-pressed': on,
        onClick: () => {
          toggle()
          onClick()
        },
        ...props,
      }
    },
  }
}

function App() {
  const {on, getTogglerProps} = useToggle()
  return (
    <div>
      <Switch on={on} {...getTogglerProps()} />
      <hr />
      <button
        aria-label="custom-button"
        {...getTogglerProps({
          onClick: () => {
            console.log(
              `Calling inside a function that's passed to prop getters,
               to execute apart from the defaults provided by prop collections.`,
            )
          },
        })}
      >
        {on ? 'on' : 'off'}
      </button>
    </div>
  )
}
3 Likes

#Day74 of #100daysofcode

Today I learned about the State Reducer React Pattern, I have started implementing it in an example component and I think what it basically does is, it inverts the control over the state management of our hook to the developer utilizing it. I will post a detailed implementation for this pattern tomorrow.

1 Like

#Day75 of #100daysofcode

Yay! Today I implemented the State Reducer React Pattern in a custom React Hook.
Let’s see this pattern in action.

Suppose we have a state reducer function as below, which basically updates the state based on the “actionType” provided to it.

function toggleReducer(state, {type, initialState}) {
  switch (type) {
    case 'toggle': {
      return {on: !state.on}
    }
    case 'reset': {
      return initialState
    }
    default: {
      throw new Error(`Unsupported type: ${type}`)
    }
  }
}

Now, by using this reducer we are going to implement a custom hook called useToggle that we can use across other components in our app.

function useToggle({initialOn = false, reducer = toggleReducer} = {}) {
  const {current: initialState} = React.useRef({on: initialOn})
  const [state, dispatch] = React.useReducer(reducer, initialState)
  const {on} = state

  const toggle = () => dispatch({type: 'toggle'})
  const reset = () => dispatch({type: 'reset', initialState})

  ... ...

  return {
    on,
    reset,
    toggle,
    getTogglerProps,
    getResetterProps,
  }
}

Finally, we are going to use this hook in our App component.

function App() {
  const {on, getTogglerProps, getResetterProps} = useToggle()
  return (... Some JSX here ...)
}

But what if I (as the user of this hook) want to implement a custom reducer instead of the default toggleReducer used by the useToggle hook?

I can simply pass a custom reducer in the argument to the useToggle hook like this:

function App() {
  const [timesClicked, setTimesClicked] = React.useState(0)
  const clickedTooMuch = timesClicked >= 4

  function toggleStateReducer(state, action) {
    switch (action.type) {
      case 'toggle': {
        if (clickedTooMuch) {
          return {on: state.on}
        }
        return {on: !state.on}
      }
      case 'reset': {
        return {on: false}
      }
      default: {
        throw new Error(`Unsupported type: ${action.type}`)
      }
    }
  }

  const {on, getTogglerProps, getResetterProps} = useToggle({
    reducer: toggleStateReducer,
  })

  return (... Some JSX here ...)
}

And that just works flawlessly.

3 Likes

#Day76 of #100daysofcode

Today I learned about the Control Props React Pattern, it’s been the toughest React Pattern I have ever come across. I am still wrapping my head around this. I have tried an example using this pattern but it seems like I have to revisit this concept, again and again, to understand it fully.
Therefore I will give it a few more tries tomorrow and hopefully I will be able to grasp it properly.

3 Likes

Hi @SourabhBagrecha,

Congrats on passing the 3/4 mark i(75 days!) in your challenge! :fire:

You’ve earned the rare Coding Legend badge!

Thanks for sharing … keep up the great journey!

Regards,
Stennie

5 Likes

#Day77 of #100daysofcode

Thank you Captain @Stennie_X :saluting_face: .
As we saw in the State Reducer pattern that we can provide custom reducers to our hook and that will in return update our state accordingly, but what if we want to give this state control to a component that can mutate the state of this component(hook) from outside.
So, here we are with our Control Props React Pattern implementation, that’s exactly applicable in the above said situations.

function useToggle({
  initialOn = false,
  reducer = toggleReducer,
  on: controlledOn,
  onChange,
} = {}) {
  const {current: initialState} = React.useRef({on: initialOn})
  const [state, dispatch] = React.useReducer(reducer, initialState)

  const onIsControlled = controlledOn != null
  const on = onIsControlled ? controlledOn : state.on

  const dispatchWithOnChange = action => {
    if (!onIsControlled) {
      dispatch(action)
    }
    onChange(reducer({...state, on}, action), action)
  }

  const toggle = () => dispatchWithOnChange({type: actionTypes.toggle})
  const reset = () => dispatchWithOnChange({type: actionTypes.reset, initialState})

 ... some prop collection implementation here...

  return {
    on,
    reset,
    toggle,
    getTogglerProps,
    getResetterProps,
  }
}

function Toggle({on: controlledOn, onChange}) {
  const {on, getTogglerProps} = useToggle({on: controlledOn, onChange})
  const props = getTogglerProps({on})
  return <Switch {...props} />
}

function App() {
  const [bothOn, setBothOn] = React.useState(false)
  const [timesClicked, setTimesClicked] = React.useState(0)

  function handleToggleChange(state, action) {
    if (action.type === actionTypes.toggle && timesClicked > 4) {
      return
    }
    setBothOn(state.on)
    setTimesClicked(c => c + 1)
  }

  function handleResetClick() {
    setBothOn(false)
    setTimesClicked(0)
  }

  return (
    <div>
        <Toggle on={bothOn} onChange={handleToggleChange} />
        <Toggle on={bothOn} onChange={handleToggleChange} />
        .... some more JSX here ...
    </div>
  )
}

In the above implementation, we are making both the <Toggle/> components synchronized.

2 Likes

#Day78 of #100daysofcode

Today I took my component that implemented the Control Prop React Pattern to the next level.
If you have worked with React’s Input elements, then you might be aware of the following warning:

Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.

We should throw a similar warning whenever a developer is misusing our controlled props.
As per Kent C Dodds, there can be 3 scenarios where it will be considered a misuse:

  1. When on/value prop has some value, but onChange prop has no function assigned to it.

  2. Passing a value for on but later passing undefined or null

  3. Passing undefined or null but later passing a value

function useToggle({
  initialOn = false,
  reducer = toggleReducer,
  on: controlledOn,
  onChange,
} = {}) {
  const {current: initialState} = React.useRef({on: initialOn})
  const [state, dispatch] = React.useReducer(reducer, initialState)

  const onIsControlled = controlledOn != null
  const on = onIsControlled ? controlledOn : state.on

  const hasOnChange = !(!onChange);
  useEffect(() => {
  // here we are checking if on is controlled and the component has no onChange function passed to it, if so we are throwing a warning.
    if(onIsControlled && !hasOnChange){
      console.error(`An \`on\` prop was provided to useToggle without an \`onChange\` handler. This will render a read-only toggle. If you want it to be mutable, use \`initialOn\`. Otherwise, set either \`onChange\` or \`readOnly\`.`)
    }
  }, [onIsControlled, hasOnChange]) 

  const dispatchWithOnChange = action => {
    if (!onIsControlled) {
      dispatch(action)
    }
    onChange?.(reducer({...state, on}, action), action)
  }

  const toggle = () => dispatchWithOnChange({type: actionTypes.toggle})
  const reset = () => dispatchWithOnChange({type: actionTypes.reset, initialState})

 ... some prop collection implementation here...

  return {
    on,
    reset,
    toggle,
    getTogglerProps,
    getResetterProps,
  }
}
5 Likes

#Day79 of #100daysofcode

Today I implemented some capabilities to our Control Prop component to throw an error when a component goes from uncontrolled to a controlled (or vice versa) state.
Typically, React would throw the following error in the above-mentioned state:

Warning: A component is changing an uncontrolled input of type undefined to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

Here’s what our custom hook would look like when the above situation needs to be handled.

function useToggle({
  initialOn = false,
  reducer = toggleReducer,
  onChange,
  on: controlledOn,
  readOnly = false,
} = {}) {
  const {current: initialState} = React.useRef({on: initialOn})
  const [state, dispatch] = React.useReducer(reducer, initialState)

  const onIsControlled = controlledOn != null
  const on = onIsControlled ? controlledOn : state.on

  const {current: onWasControlled} = React.useRef(onIsControlled)
  React.useEffect(() => {
    if(onIsControlled && !onWasControlled){
      console.error(`\`useToggle\` is changing from uncontrolled to be controlled. Components should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled \`useToggle\` for the lifetime of the component. Check the \`on\` prop.`)
    }

    if(!onIsControlled && onWasControlled){
      console.error(`\`useToggle\` is changing from controlled to be uncontrolled. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled \`useToggle\` for the lifetime of the component. Check the \`on\` prop.`)
    }
  }, [onIsControlled, onWasControlled])

  const hasOnChange = Boolean(onChange)
  React.useEffect(() => {
    if(!hasOnChange && onIsControlled && !readOnly){
      console.error(`An \`on\` prop was provided to useToggle without an \`onChange\` handler. This will render a read-only toggle. If you want it to be mutable, use \`initialOn\`. Otherwise, set either \`onChange\` or \`readOnly\`.`);
    }
  }, [hasOnChange, onIsControlled, readOnly])

  function dispatchWithOnChange(action) {
    if (!onIsControlled) {
      dispatch(action)
    }
    onChange?.(reducer({...state, on}, action), action)
  }

  const toggle = () => dispatchWithOnChange({type: actionTypes.toggle})
  const reset = () =>
    dispatchWithOnChange({type: actionTypes.reset, initialState})

... some prop collection implementation here...

  return {
    on,
    reset,
    toggle,
    getTogglerProps,
    getResetterProps,
  }
}
2 Likes

#Day80 of #100daysofcode

Today I extracted those error handling functionalities into a custom hook that can be used across different other hooks and components without re-writing the core logic behind it.
So here are our custom hooks:

function useControlledSwitchWarning(
  controlPropValue,
  controlPropName,
  componentName,
) {
  const isControlled = controlPropValue != null
  const {current: wasControlled} = React.useRef(isControlled)

  React.useEffect(() => {
    if(isControlled && !wasControlled){
      console.error(`\`useToggle\` is changing from uncontrolled to be controlled. Components should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled \`useToggle\` for the lifetime of the component. Check the \`on\` prop.`)
    }

    if(!isControlled && wasControlled){
      console.error(`\`useToggle\` is changing from controlled to be uncontrolled. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled \`useToggle\` for the lifetime of the component. Check the \`on\` prop.`)
    }
  }, [componentName, controlPropName, isControlled, wasControlled])
}

function useOnChangeReadOnlyWarning(
  controlPropValue,
  controlPropName,
  componentName,
  hasOnChange,
  readOnly,
  readOnlyProp,
  initialValueProp,
  onChangeProp,
) {
  const isControlled = controlPropValue != null
  React.useEffect(() => {
    if(!hasOnChange && isControlled && !readOnly){
      console.error(`An \`on\` prop was provided to useToggle without an \`onChange\` handler. This will render a read-only toggle. If you want it to be mutable, use \`initialOn\`. Otherwise, set either \`onChange\` or \`readOnly\`.`);
    }
  }, [
    componentName,
    controlPropName,
    isControlled,
    hasOnChange,
    readOnly,
    onChangeProp,
    initialValueProp,
    readOnlyProp,
  ])
}

Now, we can utilize these hooks just by passing arguments to them. One thing to note here is that we don’t need these warnings when our app is in production mode, these warnings are just there to alert the developer using them in order to avoid any anti-pattern and misuse of this hook.
Since we extracted our core logic into custom hooks, this gives us the opportunity to implement Dead Code Elimination.
Since there is no significant value in displaying an error for misuse of our hook to the end-user, we can simply avoid these checks, and by avoiding these checks(using dead code elimination) we can leverage the following benefits:

  • It shrinks program size
  • It eliminates unnecessary computation
  • End User will not be able to see these warnings, which is often the intended behavior. (because these errors are only meant for developers utilizing these hooks in their components)

Take a look at the following implementation to see the above in action:

if (process.env.NODE_ENV !== 'production') {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useControlledSwitchWarning(controlledOn, 'on', 'useToggle')
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useOnChangeReadOnlyWarning(
      controlledOn,
      'on',
      'useToggle',
      Boolean(onChange),
      readOnly,
      'readOnly',
      'initialOn',
      'onChange',
    )
  }
3 Likes

#Day81 of #100daysofcode

Today I learned about performance optimization in React.
Sometimes during the initial render of the component(parent), we may not want to load assets(or components) that are heavier in size and might take significant time to load which can affect the User Experience of our app. And maybe we just want to load it conditionally or wait for the user to perform some action.
In such cases, we can utilize the lazy loading API of React. It provides us with a <Suspense> wrapper that allows us to provide a fallback UI that appears while the component is getting loaded.
Let’s take a look at an example to understand this better, suppose we want to display a globe that is very heavy in size and we just want to show it when the user checks the checkbox for show globe :white_check_mark: . We can utilize Suspense in the following manner, such that the very heavy Globe component doesn’t get loaded in the initial render hence the user doesn’t have to wait even in very slow internet connections:

import * as React from 'react'
const loadGlobe = () => import('../globe')
const Globe = React.lazy(loadGlobe)

function App() {
  const [showGlobe, setShowGlobe] = React.useState(false)

  return (
    <div
      style={{
        display: 'flex',
        alignItems: 'center',
        flexDirection: 'column',
        justifyContent: 'center',
        height: '100%',
        padding: '2rem',
      }}
    >
      <label style={{marginBottom: '1rem'}}>
        <input
          type="checkbox"
          checked={showGlobe}
          onChange={e => setShowGlobe(e.target.checked)}
          onMouseOver={loadGlobe}
          onFocus={loadGlobe}
        />
        {' show globe'}
      </label>
      <div style={{width: 400, height: 400}}>
        <React.Suspense fallback={<div>loading...</div>}>
          {showGlobe ? <Globe /> : null}
        </React.Suspense>
      </div>
    </div>
  )
}

export default App
2 Likes

#Day82 of #100daysofcode

Today I learned about the useMemo hook, which is basically providing us a way to preserve the results of any expensive calculations/computations as long as the dependencies provided to it don’t change.
It can dramatically improve the performance of our app.
Let’s take an example where we are performing some complex computations by calculating the distance between two points x and y.

function Distance({x, y}) {
  const distance = calculateDistance(x, y)
  return (
    <div>
      The distance between {x} and {y} is {distance}.
    </div>
  )
}

Now, whenever this component gets re-rendered, the compute-intensive distance calculation will take place. This will eventually slow down our app, and when there are multiple components in our app utilizing some compute-intensive functions that are getting executed for the same values just because the component has to re-render, it can significantly hamper our user experience.

Therefore, we need a way to optimize our app in such a way that it memoizes the results and can avoid re-calculating them on every render.

useMemo to the rescue!

function Distance({x, y}) {
  const distance = React.useMemo(() => calculateDistance(x, y), [x, y])
  return (
    <div>
      The distance between {x} and {y} is {distance}.
    </div>
  )
}

Now the above component would smartly memoize the results and utilize them on every render, until and unless there is a change in any one of those dependencies(x or y).

2 Likes

#Day83 of #100daysofcode

Today I learned about the Performance tab in the browser developer tools. I learned some really new and exciting features which we can utilize to simulate real-world scenarios, like CPU throttling and internet speed.
Not only that, but we can also record some events and analyze the performance of our app function-by-function and a really nice and insightful graph will be shown to us that visualizes the performance of our app.

Chrome Developer Tools never ceases to impress me.

As you can see from the visualization that matchSorter is the most expensive function in my app is matchSorter because it has the biggest width among all the functions.
Once I click on that, I get a summary of the aggregated time for the concerned function, and gives us a really nice breakdown of how the total time is getting distributed.

3 Likes

#Day84 of 100daysofcode

Since I have been learning about React App’s performance for a few days, I can not conclude this without mentioning Web Workers.

Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface.

It’s obvious how this Web Worker API can help us in increasing our App’s performance.

Let’s see this in action:
In this example, we are going to use the Workerize package to ensure that we are offloading these heavy calculations that are not directly responsible for Browser’s UI paint process.

Previously we were importing the heavy function directly into our component like this:

import {getItems} from '../filter-cities'

But the above is not utilizing the Web Worker to execute.

We can create a new layer over the ../filter-cities file which will be Workerizing this function:

//FileName: workerized-filter-cities.js
import makeFilterCitiesWorker from 'workerize!./filter-cities'

const {getItems} = makeFilterCitiesWorker()

export {getItems}

And then we will use the above file to consume a getItems function that is utilizing the Web Worker in our component in the following manner:

import {getItems} from '../workerized-filter-cities'
4 Likes

#Day85 of #100daysofcode

Today I learned about the React.memo higher order component.
In React, a component can re-render for any of the following reasons:

  1. Its props change
  2. Its internal state changes
  3. It is consuming context values which have changed
  4. Its parent re-renders

But what if we don’t want a particular component to re-render unless and until there’s a good reason to re-render it(like the update needs to reflect on the DOM)?

For the above-mentioned reason, we can simply use React.memo to ensure that the component only gets re-rendered until and unless its own props get changed.
Here’s an example implementation on how we can do the same on a ListItem which is a child component of a really long List Component:

function ListItem({
  getItemProps,
  item,
  index,
  selectedItem,
  highlightedIndex,
  ...props
}) {
  const isSelected = selectedItem?.id === item.id
  const isHighlighted = highlightedIndex === index
  return (
    <li
      {...getItemProps({
        index,
        item,
        style: {
          fontWeight: isSelected ? 'bold' : 'normal',
          backgroundColor: isHighlighted ? 'lightgray' : 'inherit',
        },
        ...props,
      })}
    />
  )
}

The above component will re-render whenever its parent gets re-rendered, and that can cause serious performance issues when the list is significantly long.
Now, we can simply wrap this ListItem component using the following syntax:

ListItem = React.memo(ListItem)

And this will avoid all the unnecessary re-render we just discussed.

3 Likes

#Day86 of 100daysofcode

Today I learned comparator custom comparator functions in React.memo HOC(higher-order components).
Because of our React.memo optimization yesterday, our list items won’t get re-render if their parent re-renders.

We can see this in React Devtools’ Profiler as below:

Suppose we want to have a feature in our app, where any time the user hovers over any one of those list items, they will get highlighted as shown below:

Now every time a user hovers over any list item the complete list along with every list item in it will get re-rendered because every ListItem component has a prop called highlightedIndex as we have seen yesterday.
Let’s take a look at our Profiler again when we hover over any item in the list:

But we don’t want to re-render all the items in the list because all of them have no visible UI updates to reflect in the DOM. We just want to update the list item that has been highlighted or which was highlighted before but it’s no longer highlighted.

For cases like these where the component’s props have changed but we still want to re-render only when a specific condition takes place, we can use Comparator Functions.
Let’s see it in action:

ListItem = React.memo(ListItem, (prevProps, nextProps) => {
  if (prevProps.getItemProps !== nextProps.getItemProps) return false
  if (prevProps.item !== nextProps.item) return false
  if (prevProps.index !== nextProps.index) return false
  if (prevProps.selectedItem !== nextProps.selectedItem) return false

  if (prevProps.highlightedIndex !== nextProps.highlightedIndex) {
    const wasPrevHighlighted = prevProps.highlightedIndex === prevProps.index
    const isHighlightedNow = nextProps.highlightedIndex === nextProps.index
    return wasPrevHighlighted === isHighlightedNow
  }
})

In the above implementation, we are passing a comparator function as the second argument to React.memo that will accept two parameters ( prepProp & nextProp ).
Both these parameters will include prop state(before and after) whenever a re-render get triggered for the concerned component.
Here we are checking if any of the props other than highlightedIndex change, we simply need to re-render this component, so we are simply returning false to tell React that this component need to re-render.
But what about the highlightedIndex prop, well we are still checking whether it is changed or not but along with that we will also check whether the component that is in consideration has anything to do with the change in the highlightedIndex. If the listItem doesn’t need to be highlighted or un-highlighted based upon its own index we won’t re-render it, otherwise we will re-render it.

Now, I will hover over a few list items one-by-one and you can see in the profiler that only the affected components(as per the highlightedIndex prop) in the list are being re-rendered, otherwise, all the other listItem will remain as-is, which is a great performance boost:

3 Likes