Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Why do you need to… Use unidirectional data flow Avoid calling hooks conditionally Store state using the useState hook

Slide 3

Slide 3 text

Conditionally call React hooks Build impure components Pull in a second UI library

Slide 4

Slide 4 text

About me Senior Software Engineer building Microsoft Loop Worked on Edge, Chrome, and Firefox React developer for 8+ years tigeroakes.com @notwoods.bsky.social @not_woods

Slide 5

Slide 5 text

The art of best practices for React performance ignoring

Slide 6

Slide 6 text

* * * *

Slide 7

Slide 7 text

CAUTION! Use your best judgement, evaluate tradeoffs, and communicate. Photo by Markus Spiske on Unsplash

Slide 8

Slide 8 text

THE PLAN 01 When does React rerender? 02 How to isolate expensive hooks Using impure components 03 How to only update leaf components Using a different UI library

Slide 9

Slide 9 text

01 When does React rerender? Photo by Noah Boyer on Unsplash

Slide 10

Slide 10 text

Every rerender starts with a state change. State change Component re-renders

Slide 11

Slide 11 text

Every rerender starts with a state change. State change Component re-renders Descendant components re-render

Slide 12

Slide 12 text

function MyCounter() { const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1); return (
Increment
); } function Title() { return

# of rules broken

; } function CounterDisplay({ count }) { return {count}; }

Slide 13

Slide 13 text

function MyCounter() { const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1); return (
Increment
); } function Title() { return

# of rules broken

; } function CounterDisplay({ count }) { return {count}; }

Slide 14

Slide 14 text

function MyCounter() {

# of rules broken

{1} Increment
}

# of rules broken

{0} Increment
vs

Slide 15

Slide 15 text

App MyCounter state: count (0) Title CounterDisplay props: count (1) (2) (3) (4) Increment

Slide 16

Slide 16 text

App MyCounter state: count (0) Title CounterDisplay props: count (1) (2) (3) (4) Increment React.memo() React.memo()

Slide 17

Slide 17 text

React Profiler: Highlight renders

Slide 18

Slide 18 text

React Profiler: Highlight renders

Slide 19

Slide 19 text

React Profiler: Why did it render?

Slide 20

Slide 20 text

https://www.joshwcomeau.com /react/why-react-re-renders/

Slide 21

Slide 21 text

Just always write performant components!

Slide 22

Slide 22 text

Every rerender starts with a state change. State change Component re-renders

Slide 23

Slide 23 text

02 Isolating expensive hooks Isolate state changes with side effect components Photo by Sam Pearce-Warrilow on Unsplash

Slide 24

Slide 24 text

function useGetData() { const [state, setState] = useState(); useEffect(() => { fetchData().then(setState); }, []); return state; } function App() { const data = useGetData(); return ; }

Slide 25

Slide 25 text

function useGetData() { const [state, setState] = useState(); const [otherState, setOtherState] = useState(); useEffect(() => { fetchData().then(setState); }, []); useEffect(() => { fetchOtherData(state.id).then(setOtherState); }, [state.id]); return otherState; } function App() { const data = useGetData(); return ; }

Slide 26

Slide 26 text

function useGetData() { const [otherState, setOtherState] = useState(); useEffect(() => { fetchData() .then(state => fetchOtherData(state.id)) .then(setOtherState) }, []); return otherState; } function App() { const data = useGetData(); return ; }

Slide 27

Slide 27 text

function ExpensiveEffect(props) { const otherState = useGetData(); useEffect(() => { props.setState(otherState); }, [otherState]); return null; } function App() { const [data, setData] = useState(); return <> >; }

Slide 28

Slide 28 text

App state: data ExpensiveEffect props: setState UI props: data

Slide 29

Slide 29 text

5x speedup Refactoring in Microsoft Loop 1x per state change

Slide 30

Slide 30 text

return ( <> {shouldLoad && ( )} > );

Slide 31

Slide 31 text

const LazyExpensiveEffect = React.lazy(() => import('./ExpensiveEffect’) ); return ( <> > );

Slide 32

Slide 32 text

@Composable fun loadData() { val (state, setState) = remember { mutableStateOf() } SideEffect { fetchData().then(::setState) } return state } @Composable fun App() { … }

Slide 33

Slide 33 text

App state: data ExpensiveEffect props: setState UI props: data

Slide 34

Slide 34 text

03 Only updating leaf components Pull state management out of React Photo by Jakob Rosen on Unsplash

Slide 35

Slide 35 text

Sidebar Item Date Item Date Item Date

Slide 36

Slide 36 text

useState

Slide 37

Slide 37 text

Svelte

Slide 38

Slide 38 text

import { writable } from 'svelte/store'; let store = writable(initialState); store.set(newState); store.update(oldState => newState);

Slide 39

Slide 39 text

import { writable, derived, } from 'svelte/store'; import {} from 'svelte';

Slide 40

Slide 40 text

import { writable } from 'svelte/store'; function Sidebar(props) { const store = React.useMemo(() => writable()); return <> {props.items.map(item => )} > }

Slide 41

Slide 41 text

import { writable } from 'svelte/store'; let store = writable(0); store.set(1); store === store;

Slide 42

Slide 42 text

import { useSyncExternalStore } from 'react'; import { get } from 'svelte/store'; // useSyncExternalStore internally uses // useState and useEffect const state = useSyncExternalStore( store.subscribe, () => get(store) );

Slide 43

Slide 43 text

Sidebar Item skipped Date Item skipped Date Item skipped Date

Slide 44

Slide 44 text

Svelte Stores ● Store is a constant reference passed around with props or context. ● Store’s value can be changed without re-rendering React tree. ● Translated into React state using the useSyncExternalStore hook.

Slide 45

Slide 45 text

function Sidebar(props) { const store = React.useMemo(() => writable()); return <> {props.items.map(item => )} > }

Slide 46

Slide 46 text

import { writable, readable } from 'svelte/store'; class DataModel { #state = writable(); get state() { return readable(this.#state); } performUpdate() { this.#state.set('done') } }

Slide 47

Slide 47 text

Redux ● Store is a constant reference passed around with . ● Store’s value can be changed without re-rendering React tree. ● Translated into React state using the useSelector hook.

Slide 48

Slide 48 text

React.useReducer ● Reducer value is stored as one giant React state object. ● Changing reducer’s value will re-render React tree. ● State is translated at the top of the React tree once, instead of in the leaf components.

Slide 49

Slide 49 text

Could you use Signals? Yes.

Slide 50

Slide 50 text

import { readable } from 'svelte/store'; const mediaQuery = window.matchMedia('(max-width: 640px)'); const mediaQueryStore = readable( mediaQuery.matches, function onFirstListenerStart(set) { const listener = () => set(mediaQuery.matches); listener(); mediaQuery.addEventListener('change', listener); // return cleanup function, similar to useEffect return function onLastListenerStop() { mediaQuery.removeEventListener('change', listener); } } );

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

CREDITS: This presentation template was created by Slidesgo, and includes icons by Flaticon, and infographics & images by Freepik THANKS! https://loop.microsoft.com tigeroakes.com @notwoods.bsky.social @not_woods Photo by Fabio Spinelli on Unsplash