Previously on
React Conf
When it happens
🔍
user interacting
async request
subscription
- State changes
- Parent's component render
- Context changes
- 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; // Your "loader"
return <FreezedButton systemLocked={systemLocked} />
}
const FreezedButton = memo(({ systemLocked }) => {
// The rest of your rendering logic
return <ExpensiveTree isSystemLocked={systemLocked} />;
});
const button = () => {
let appContextValue = useContext(AppContext);
let systemLocked = appContextValue.systemLocked; // Your "loader"
return useMemo(() => {
// The rest of your rendering logic
return <ExpensiveTree 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; // Your "loader"
return <FreezedButton systemLocked={systemLocked} />
}
const FreezedButton = memo(({ systemLocked }) => {
// The rest of your rendering logic
return <ExpensiveTree 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
Don't Forget React
By Khrystyna Landvytovych
Don't Forget React
- 413