React Conf

When it happens

๐Ÿ”

user interacting

async request

subscriptionย 

  1. State changes
  2. Parent's component render
  3. Context changes
  4. Hooks changes
React.memo()

Causes to:

Why do we care?

โœ‹

๐Ÿ›‘

๐Ÿš€

const ChildComponent = React.memo(({ filterData }) {
  console.log('child rendering ...')
  return(
    <>ChildStuff</>
  )
})


const ParentComponent = () => {
  const filterData = () => {
      // do something 
    }
    
  const handleClick = useCallback(filterData, [])
    return <ChildComponent filterData = {handleClick} />
}
https://www.insta.com/user-account?userId=34124
 const { search } = useLocation();

 const queryParams = useMemo(() => new URLSearchParams(search),[search]);

 const closeUserDetailModal = useCallback(() => {
   const parameter = queryParams.get('userId');

    if (parameter) {
      queryParams.delete('userId');
    }
    history.replace({
      search: queryParams?.toString(),
    });
    refreshPage();
  }, [history, queryParams, refreshPage]);

  useEffect(() => {
    const userId = queryParams.get('userId');
    if (userId) {
      setState(() => ({ selectedBranch: Number(userId) }));
    }
  }, [queryParams]);
const button = () => {
  let systemLocked = useContext(Loader);
  // The rest of your rendering logic
  return <FreezedComponent isSystemLocked={systemLocked} />;
}
const button = () => {
  let appContextValue = useContext(AppContext);
  let systemLocked = appContextValue.systemLocked; // My "loader" property
  return <FreezedButton systemLocked={systemLocked} />
}

const FreezedButton = memo(({ systemLocked }) => {
  // The rest of your rendering logic
  return <FreezedComponent isSystemLocked={systemLocked} />;
});
const button = () => {
  let appContextValue = useContext(AppContext);
  let systemLocked = appContextValue.systemLocked; // My "loader"

  return useMemo(() => {
    // The rest of your rendering logic
    return <FreezedComponent isSystemLocked={systemLocked} />;
  }, [systemLocked])
}

Memorization

App

//...
    return {
        swap: React.useCallback(swap, [updateValues, name, control]),
        move: React.useCallback(move, [updateValues, name, control]),
        prepend: React.useCallback(prepend, [updateValues, name, control]),
        append: React.useCallback(append, [updateValues, name, control]),
        remove: React.useCallback(remove, [updateValues, name, control]),
        insert: React.useCallback(insert, [updateValues, name, control]),
        update: React.useCallback(update, [updateValues, name, control]),
        replace: React.useCallback(replace, [updateValues, name, control]),
        fields: React.useMemo(
        () =>
            fields.map((field, index) => ({
            ...field,
            [keyName]: ids.current[index] || generateId(),
            })) as FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName>[],
        [fields, keyName],
    )

๐Ÿ’ญ

๐Ÿ˜ตโ€๐Ÿ’ซ

useMemo === useCallback

memorization

overusing useMemo

solid understanding

figure out best practicesย 

๐Ÿ‘‹๐Ÿป

Khrystyna Landvytovych

FE Engineer, SoftServe

๐Ÿ’Š

Approve
Simplify 
Customize your hook

1. ย plain JavaScript object

2. value can be accessed, modified

3. should be persist

First step

๐Ÿค“

// useState returns an array. Index zero is the current value

const ref = useState({ current: initialValue })[0]

useRef?

๐Ÿคจ

Recompute the memoized value only when one of the dependencies has changed

Shallow Compare

๐Ÿง

const shallowEquals = (prevDeps, currentDeps) => {
  if (!Array.isArray(prevDeps) || !Array.isArray(currentDeps)) {
    return false;
  }

  if (prevDeps.length !== currentDeps.length) {
    return false;
  }

 
  for(item of prevDeps) {
    if (!Object.is(prevDeps[item], currentDeps[item])) {
      return false;
    }
  }

  return true;
};


const useMyMemo = (create, dependencies) => {
  const val = useRef(create());
  const prevDependencies = useRef([]);

  if (!shallowEquals(dependencies, prevDependencies.current)) {
    val.current = create();
    prevDependencies.current = [...dependencies];
  }
  return val;

};
# PRESENTING CODE

1

Customise useRef by useState

2

ย 

useMemo similar to useRef

4

What about useCallBack

3

Combine useRef with auto-update functionality

useCallback((e) => onClick(id, e.target.value), [onClick, id]);

// is equivalent to

useMemo(() => (e) => onClick(id, e.target.value), [onClick, id]);

Where we get wrong?

const App = () => {
  const [state, setState] = useState(1);

  return (
    <div className="App">
      <button onClick={
     	() => setState(state + 1)}> 
    		click to re-render {state}
      </button>
      <br />
      <Page />
    </div>
  );
};
const Page = () => <Item />;
const PageMemoized = React.memo(Page);

Only one way

const myMovieFilter = React.useCallback(film => {
  setMovies(allMovies => allMovies.filter(el => el.genre !== 'Horror'))
}, [])

// equals to 

const myMovieFilter = film => {
  setMovies(allMovies => allMovies.filter(el => el.genre !== 'Horror'))
}
const unnecessaryCallback = React.useCallback(myMovieFilter, [])

Extra check for values

const myMovieFilter = React.useCallback(film => {
  setMovies(allMovies => allMovies.filter(el => el.genre !== 'Horror'))
}, [])

const MoviesList = ({ movies }) => {
  const content = useMemo(() => {
    const sortedMovies = orderBy(movies, 'name', sort);

    return sortedMovies.map((movie) => <Item movie={movie} key={movie.id} />);
  }, [movies, sort]);

  return content;
};

Unnecessary with primitive values


Array sorting perf:  
2
Overall performance:  
8.300000004470348
Array filtering perf:  
0.8999999985098839
Overall performance:  
9.700000002980232
Array filtering perf:  
1.7999999970197678
Overall performance:  
16.5
Array sorting perf:  
53.89999999851

๐Ÿ˜

๐Ÿ˜ฆ

๐Ÿ˜ต

import React, { useState, useMemo } from "react";

const Child = ({ value }) => {
  console.log("Child re-renders", value.value);
  return <>{value.value}</>;
};

const App = () => {
  const [state, setState] = useState(1);

  const onClick = () => {
    setState(state + 1);
  };

  const memoValue = useMemo(() => ({ value: "I still render" }), []);

  return (
    <>
      <p>Child will re-render even if its value is memoized</p>

      <button onClick={onClick}>click here</button>
      
      <Child value={memoValue} />
    </>
  );
};

export default App;
if (!instanceRef.current) {
        instanceRef.current = createTableInstance<
          TData,
          TValue,
          TFilterFns,
          TSortingFns,
          TAggregationFns
        >(options, rerender)
      }

return instanceRef.current
UseRef 

is an answear

const button = () => {
  let appContextValue = useContext(AppContext);
  let systemLocked = appContextValue.systemLocked; // My "loader"
  return <FreezedButton systemLocked={systemLocked} />
}

const FreezedButton = memo(({ systemLocked }) => {
  // The rest of your rendering logic
  return <FreezedComponent isSystemLocked={systemLocked} />;
});

Refactoring time โœจ

const useIsSystemLocked = () => {
  const {
    data: { getSystemStatus } = { getSystemStatus: SystemStatus.Unlocked },
    networkStatus,
    refetch: refetchSystemStatusQuery,
  } = useQuery<SystemStatusQuery, SystemStatusQueryVariables>(systemStatusQuery);

  const systemLocked = isSystemLocked(getSystemStatus);
  const previousSystemLocked = usePrevious(systemLocked);

  return {
      previousSystemLocked,
      systemLocked,
    }
};

Moral

Most of situations we can skip React.memo's friends

Make Performance optimizations with rensposibility.

Stay with ๐Ÿ‡บ๐Ÿ‡ฆ

@croftyland

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป

Copy of Don't Forget React

By Khrystyna Landvytovych