From 08f02f986c29ce090a4261bbc42a9737d1330fcd Mon Sep 17 00:00:00 2001 From: adriancuadrado <29214635+adriancuadrado@users.noreply.github.com> Date: Tue, 12 Aug 2025 06:25:30 +0200 Subject: [PATCH 1/2] Update and rename 0000-template.md to 0000-api-to-expose-internals.md --- 0000-api-to-expose-internals.md | 104 ++++++++++++++++++++++++++++++++ 0000-template.md | 68 --------------------- 2 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 0000-api-to-expose-internals.md delete mode 100644 0000-template.md diff --git a/0000-api-to-expose-internals.md b/0000-api-to-expose-internals.md new file mode 100644 index 00000000..a1da3eb2 --- /dev/null +++ b/0000-api-to-expose-internals.md @@ -0,0 +1,104 @@ +- Start Date: 2025-08-12 +- RFC PR: (leave this empty) +- React Issue: (leave this empty) + +# Summary + +A way to expose some React internals so it becomes easier to develop tools to debug and interact with React applications. + +# Basic example + +A global variable like `__REACT_DEVTOOLS_GLOBAL_HOOK__` but it is meant to be used by other tools and not just React Devtools. + +# Motivation + +Why are we doing this? + +To allow other developers create tools like [react-scan](https://www.reddit.com/r/reactjs/comments/1h70kfg/reverse_engineering_react_internals/) without running the risk of having your code broken at any time. + +What use cases does it support? + +Developers have to test their code constantly with each react release regardless of whether it is a release that introduces major breaking changes or minor backward compatible ones. It would be nice to introduce an api with it's own versioning scheme that we can rely on. + +What is the expected outcome? + +An API that exposes some basic functionality that might be useful to create tools that interact with React applications separately, such as manipulating the state and props some components are rendered with, or maybe forcing rerenders or remounting. + +# Detailed design + +The global variable may be an object with a property "version" that represent the version of this api. This version is different from the version of react because a minor react version can introduce breaking changes in this api. Developers that rely on this api then would refer to some documentation that explains how to use this api in this specific version. + +This global variable might, for instance, have an "onLoad" callback function that would be called by react once react is fully loaded just in case someone wanted to rely on it but couldn't make sure to have their code executed after react. Then you'd have to check if this global object is available and has a "version" property to know if react is loaded to start using the api and if not then you'd create this object with your "onLoad" callback. + +We may also add some functions to this global object to interact with react: +- getState(component, stateIndex?): Change the state of the component of the first argument. stateIndex refers to the nth call of useState(). If omitted, it will return the state of all calls of useState() in an array. +- setState(component, stateIndex, newValue): Change the state of the component. +- resetState(component, stateIndex?): Reset the state to its default value. If no stateIndex is given, reset the state of all useState() calls. +- rerender(component): Force a rerender. +- remount(component): Force a remount. Basically, call all useEffects() again even if their dependencies didn't change. Useful, for instance, if a component makes a request to load the data that it displays and you want to reload it without refreshing the page or having to run the logic that would unmount and remount it. +- getReactRoot(): Get the root react element. You can traverse the children of the root component to get the reference that you'd pass to the other functions. + +# Drawbacks + +Why should we *not* do this? Please consider: + +- implementation cost, both in term of code size and complexity: depends on how much we want to expose. If we only wanted to allow developers to change the state of components, maybe not much. +- whether the proposed feature can be implemented in user space: it can with tools like [bippy](https://github.com/aidenybai/bippy), but it's a matter of time that they break because it relies on react internals that don't have any kind of api or versioning. +- the impact on teaching people React: this change wouldn't affect how people learn to use react, but would help greatly those who have to reverse engineer the source code to make tools that change the state of a component externally like React Devtools does. +- integration of this feature with other existing and planned features: we'd have to consider whether it would be worth it to expose some api to interact with new features from now on. +- cost of migrating existing React applications (is it a breaking change?): it's not a breaking change, so zero. + +# Alternatives + +What other designs have been considered? + +[Bippy](https://github.com/aidenybai/bippy) + +What is the impact of not doing this? + +There is this self-explanatory warning in the bippy's README: + +> [!WARNING] +> ⚠️⚠️⚠️ **this project may break production apps and cause unexpected behavior** ⚠️⚠️⚠️ +> +> this project uses react internals, which can change at any time. it is not recommended to depend on internals unless you really, _really_ have to. by proceeding, you acknowledge the risk of breaking your own code or apps that use your code. + +# Adoption strategy + +If we implement this proposal, how will existing React developers adopt it? They will have to refactor the part of their code that relies on react internals that are exposed by this api. + +Is this a breaking change? + +No + +Can we write a codemod? + +No + +Should we coordinate with other projects or libraries? + +No + +# How we teach this + +What names and terminology work best for these concepts and why? How is this idea best presented? As a continuation of existing React patterns? + +TBD + +Would the acceptance of this proposal mean the React documentation must be re-organized or altered? + +Yes, we need to document this api somewhere. + +Does it change how React is taught to new developers at any level? + +No + +How should this feature be taught to existing React developers? + +TBD + +# Unresolved questions + +Optional, but suggested for first drafts. What parts of the design are still TBD? + +We need to decide what to name this global variable and what to include in the first versions. diff --git a/0000-template.md b/0000-template.md deleted file mode 100644 index d5846023..00000000 --- a/0000-template.md +++ /dev/null @@ -1,68 +0,0 @@ -- Start Date: (fill me in with today's date, YYYY-MM-DD) -- RFC PR: (leave this empty) -- React Issue: (leave this empty) - -# Summary - -Brief explanation of the feature. - -# Basic example - -If the proposal involves a new or changed API, include a basic code example. -Omit this section if it's not applicable. - -# Motivation - -Why are we doing this? What use cases does it support? What is the expected -outcome? - -Please focus on explaining the motivation so that if this RFC is not accepted, -the motivation could be used to develop alternative solutions. In other words, -enumerate the constraints you are trying to solve without coupling them too -closely to the solution you have in mind. - -# Detailed design - -This is the bulk of the RFC. Explain the design in enough detail for somebody -familiar with React to understand, and for somebody familiar with the -implementation to implement. This should get into specifics and corner-cases, -and include examples of how the feature is used. Any new terminology should be -defined here. - -# Drawbacks - -Why should we *not* do this? Please consider: - -- implementation cost, both in term of code size and complexity -- whether the proposed feature can be implemented in user space -- the impact on teaching people React -- integration of this feature with other existing and planned features -- cost of migrating existing React applications (is it a breaking change?) - -There are tradeoffs to choosing any path. Attempt to identify them here. - -# Alternatives - -What other designs have been considered? What is the impact of not doing this? - -# Adoption strategy - -If we implement this proposal, how will existing React developers adopt it? Is -this a breaking change? Can we write a codemod? Should we coordinate with -other projects or libraries? - -# How we teach this - -What names and terminology work best for these concepts and why? How is this -idea best presented? As a continuation of existing React patterns? - -Would the acceptance of this proposal mean the React documentation must be -re-organized or altered? Does it change how React is taught to new developers -at any level? - -How should this feature be taught to existing React developers? - -# Unresolved questions - -Optional, but suggested for first drafts. What parts of the design are still -TBD? \ No newline at end of file From 66cedd37ecb52e0d77f9e5ba6bbf96ace092df25 Mon Sep 17 00:00:00 2001 From: adriancuadrado <29214635+adriancuadrado@users.noreply.github.com> Date: Tue, 12 Aug 2025 06:26:54 +0200 Subject: [PATCH 2/2] Update 0000-api-to-expose-internals.md --- 0000-api-to-expose-internals.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/0000-api-to-expose-internals.md b/0000-api-to-expose-internals.md index a1da3eb2..c3930949 100644 --- a/0000-api-to-expose-internals.md +++ b/0000-api-to-expose-internals.md @@ -31,12 +31,12 @@ The global variable may be an object with a property "version" that represent th This global variable might, for instance, have an "onLoad" callback function that would be called by react once react is fully loaded just in case someone wanted to rely on it but couldn't make sure to have their code executed after react. Then you'd have to check if this global object is available and has a "version" property to know if react is loaded to start using the api and if not then you'd create this object with your "onLoad" callback. We may also add some functions to this global object to interact with react: -- getState(component, stateIndex?): Change the state of the component of the first argument. stateIndex refers to the nth call of useState(). If omitted, it will return the state of all calls of useState() in an array. -- setState(component, stateIndex, newValue): Change the state of the component. -- resetState(component, stateIndex?): Reset the state to its default value. If no stateIndex is given, reset the state of all useState() calls. -- rerender(component): Force a rerender. -- remount(component): Force a remount. Basically, call all useEffects() again even if their dependencies didn't change. Useful, for instance, if a component makes a request to load the data that it displays and you want to reload it without refreshing the page or having to run the logic that would unmount and remount it. -- getReactRoot(): Get the root react element. You can traverse the children of the root component to get the reference that you'd pass to the other functions. +- `getState(component, stateIndex?)`: Change the state of the component of the first argument. `stateIndex` refers to the nth call of `useState()`. If omitted, it will return the state of all calls of `useState()` in an array. +- `setState(component, stateIndex, newValue)`: Change the state of the component. +- `resetState(component, stateIndex?)`: Reset the state to its default value. If no `stateIndex` is given, reset the state of all `useState()` calls. +- `rerender(component)`: Force a rerender. +- `remount(component)`: Force a remount. Basically, call all `useEffects()` again even if their dependencies didn't change. Useful, for instance, if a component makes a request to load the data that it displays and you want to reload it without refreshing the page or having to run the logic that would unmount and remount it. +- `getReactRoot()`: Get the root react element. You can traverse the children of the root component to get the reference that you'd pass to the other functions. # Drawbacks @@ -102,3 +102,4 @@ TBD Optional, but suggested for first drafts. What parts of the design are still TBD? We need to decide what to name this global variable and what to include in the first versions. +