React hook for memoizing values and callbacks anywhere in a component.
Like other hooks, you can call React.useMemo()
and React.useCallback()
only at the top of your component function and not use them conditionally.
Inline memos let us memoize anywhere without the restrictions that apply to the usage of hooks!
import { Button, TextField } from "@material-ui/core"
import React from "react"
import useInlineMemo from "use-inline-memo"
function NameForm(props) {
const memo = useInlineMemo()
const [newName, setNewName] = React.useState(props.prevName)
// Conditional return prevents calling any hook after this line
if (props.disabled) {
return <div>(Disabled)</div>
}
return (
<React.Fragment>
<TextField
label="Name"
onChange={memo.nameChange(event => setNewName(event.target.value), [])}
value={newName}
/>
<Button
onClick={memo.submitClick(() => props.onSubmit(newName), [newName])}
style={memo.submitStyle({ margin: "16px auto 0" }, [])}
>
Submit
</Button>
</React.Fragment>
)
}
npm install use-inline-memo
Everytime you want to memoize a value, call memo.X()
with an identifier X
of your choice. This identifier will be used to map the current call to values memoized in previous component renderings.
Calling useInlineMemo()
without arguments should work in all ES2015+ runtimes as it requires the ES2015 Proxy
. If you need to support older browsers like the Internet Explorer, you will have to state the memoization keys up-front:
function NameForm(props) {
// Explicit keys to make it work in IE11
const memo = useInlineMemo("nameChange", "submitClick", "submitStyle")
const [newName, setNewName] = React.useState(props.prevName)
return (
<React.Fragment>
<TextField
label="Name"
onChange={memo.nameChange(event => setNewName(event.target.value), [])}
value={newName}
/>
<Button
onClick={memo.submitClick(() => props.onSubmit(newName), [newName])}
style={memo.submitStyle({ margin: "16px auto 0" }, [])}
>
Submit
</Button>
</React.Fragment>
)
}
When not run in production, there is also a check in place to prevent you from accidentally using the same identifier for two different values. Calling memo.X()
with the same X
twice during one rendering will lead to an error.
Inline memoizers are perfect to define simple one-line callbacks in-place in the JSX without sacrificing performance.
// Before
function Component() {
const [value, setValue] = React.useState("")
const onUserInput = React.useCallback(
(event: React.SyntheticEvent) => setValue(event.target.value),
[]
)
return (
<input onChange={onUserInput} value={value} />
)
}
// After
function Component() {
const memo = useInlineMemo()
const [value, setValue] = React.useState("")
return (
<input
onChange={memo.textChange(event => setValue(event.target.value), [])}
value={value}
/>
)
}
style
props & other objectsUsing inline style props is oftentimes an express ticket to unnecessary re-renderings as you will create a new style object on each rendering, even though its content will in many cases never change.
// Before
function Component() {
return (
<Button style={{ color: "red" }} type="submit">
Delete
</Button>
)
}
// After
function Component() {
const memo = useInlineMemo()
return (
<Button style={memo.buttonStyle({ color: "red" }, [])} type="submit">
Delete
</Button>
)
}
You don't need to memoize every style object of every single DOM element, though. Use it whenever you pass an object to a complex component which is expensive to re-render.
For more background information check out FAQs: Why memoize objects?.
useInlineMemo(): MemoFunction
Call it once in a component to obtain the memo
object carrying the memoization functions.
useInlineMemo(...keys: string[]): MemoFunction
State the memoization keys explicitly if you need to support Internet Explorer where Proxy
is not available.
memo[id: string](value: T, deps: any[]): T
Works the same as a call to React.useMemo()
or React.useCallback()
, only without the wrapping callback function. That wrapping function is useful to run expensive instantiations only if we actually refresh the value, but for our use case this is rather unlikely, so we provide you with a more convenient API instead.
id
is used to map different memoization calls between component renderings.
MIT