-
Notifications
You must be signed in to change notification settings - Fork 569
RFC: useIsolation
#257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
RFC: useIsolation
#257
Conversation
|
If I understand correctly you want the equivalent of the following: Usage: import React from 'react'
export function App() {
console.log('render App')
return (
<IsolationProvider>
<Test />
</IsolationProvider>
);
}
function Test() {
console.log('render test')
const x = useIsolation(() => {
const [x, setX] = React.useState(0)
React.useEffect(
() => {
let interval = setInterval(() => setX(x => x + 1), 1000)
return () => { clearInterval(interval) }
},
[]
)
return React.useMemo(() => x - (x % 2), [x])
})
return <p>{x}</p>
}Non-optimized user space implementation: const isolationContext = React.createContext(null)
function useIsolation(unsafeHook) {
const hook = useEvent(unsafeHook)
const [result, setResult] = React.useState(null)
const registerHook = React.useContext(isolationContext)
React.useEffect(
() => registerHook({ hook, callback: (...args) => setTimeout(() => setResult(...args), 0) }),
[]
)
return result
}
function IsolationProvider({ children }) {
console.log('render isolation provider')
const [hookInfo, setHookInfo] = React.useState([])
const registerHook = React.useCallback(
(hookInfoToBeIsolated) => {
setHookInfo(existing => existing.concat(hookInfoToBeIsolated))
return function cleanup() {
setHookInfo(existing => existing.filter(info => info !== hookInfoToBeIsolated))
}
},
[]
)
return (
<isolationContext.Provider value={registerHook}>
{children}
{hookInfo.map((info, i) =>
// key should be handled differently
<Isolation key={i} {...{ info }} />
)}
</isolationContext.Provider>
)
}
function Isolation({ info }) {
const { callback, hook } = info
const result = hook()
console.log('hook executed', result)
useCallOnChange({ ifChanged: result, callback })
return null
}
function useCallOnChange({ ifChanged, callback }) {
const changeRef = React.useRef(null)
if (changeRef.current !== ifChanged) callback(ifChanged)
changeRef.current = ifChanged
}
function useEvent(f) {
const fRef = React.useRef(null)
fRef.current = f
return React.useCallback((...args) => fRef.current(...args), [])
} |
Yes, the idea is here with 2 differences:
|
|
Thank you for the RFC. I wanted to note that we’ve had a very similar proposal planned except that we wanted to roll this behavior into the existing |
|
Wouldn't this concept of isolation also be useful for creating suspense boundaries around hooks? const useSuspenseFoo = (): Foo => React.use(fooPromise)
const ComponentThatDoesNotSuspend = () => {
const maybeFoo: Foo | undefined = React.useIsolation(() => useSuspenseFoo())
} |
I don't know, not fully sure 🤔 Like another const [value, isSuspensed] = useSuspense(() => useSuspenseFoo())But I'd move this to another RFC |
|
That said, based on the React's team direction, this RFC is highly certain to never exist. I thought a lot about this RFC and I still think it could be awesome as:
But, based on how the compiler auto optimizes things, and based on the fact we can now do const Foo = () => {
const foo = React.useContext(CustomHeavyContext).foo;
};or const Bar = () => {
const bar = useSearchParams().get("bar");
};then the variables |
This RFC related to:
useIsolationin isolation-hook and a new pattern to pass hooks to another(hook or hook creator) #168,useContextSelectorin RFC: Context selectors #119.This is about performance & memoization
>>> View rendered text <<<