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 |
I really appreciate the idea of rolling this behavior into So while I understand the motivation to unify this into |
|
@NotYoojun I agree with you: a lot of companies can't use the compiler as it's babel based (and thus too slow in their build pipelines). Also idk if unification with |
Currently |
|
I do believe the implementation of The community has been expecting this for a while: https://x.com/TkDodo/status/1741193371283026422. However, I do understand that implementation requires a certain amount of effort (and probably even massive refactoring), so I really appreciate all your effort. |
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 <<<