Open chenesan opened 5 years ago
(related to #2011)
Thanks for filing this; this is as good a place as any to discuss it.
I think that before any hook-specific APIs are added to enzyme, we'd need to finish shipping full hooks support in existing APIs - ie, wrapping components with hooks should work as you'd expect them to work.
Thanks for reply @ljharb . I agree that hooks supports for existing API is important :-) I'd also be back to finish remaining tests in #2011 recently.
And I saw people (e.g. #2012 ) try to set state of hooks with enzyme. I guess some people (include me) test hooks with enzyme even though enzyme doesn't officially support hooks and expect some ways to get / set hooks more directly. So maybe it's still worth to discuss this even when we haven't finished all the hooks tests / bugfix for existing apis. I'd like to get some feedback around the proposal and just curious to know if you have any opinion on that :-)
If it is actually possible to get at the hook information equally in both shallow and mount, at any level in the tree, that seems interesting. However, I'd want to get the opinion of the react core team before adding such an API - since if react 17 is going to make it impossible to get this info, it's not worth adding now.
Looks like there are 2 things we could do:
File an issue in React to track the release of react-denug-tools
-- If they'll publish this, then we can make sure we can leverage inspectHook
safely. Also we can try to ask for including overrideHookState
in react-debug-tools
, or at least make sure we can safely use it through devtool hook.
Look into if we could also get / set hook state in shallow renderer in other way.
I'll open an issue in React today or tomorrow :-)
(I'd also work on #2011 too. However because of some personal health issue I'm not able to code for 1 or 2 weeks. Hope to be back soon :-)
(updated: issue filed https://github.com/facebook/react/issues/15624)
According to [react core team's reply(https://github.com/facebook/react/issues/15624#issuecomment-491649500) it looks like that:
So maybe in the future it is possible to have inspectHook
; There should be no safe way to build setHookState
since we could not leverage overrideHookState
.
With current enzyme api, usually we have to get / set hook state from rendered output (e.g. print state in rendered
div
/setCount
on button). it's hard to check and change current hook state value through enzyme API (like.state()
and.setState()
for class component.). Though some (or lots of ?) people may think it's testing implementation detail, I think it's still valuable to have an api to get / set the result of hook state.There are two new apis I'd like to suggest. Hope to get some more feedbacks :-)
inspectHook()
The first is
inspectHook()
-- an api to get the whole hook stack tree in current function component node. In factinspectHookWithFiber()
is an api name from the react-debug-tool in React's internal packages for hook testing, and I'd like to port this module into enzyme.With
inspectHookWithFiber()
then we can get the whole hook stack tree in a render function and write test like this. The tree currently has the following structurewe can get the state from
value
, hook function name fromname
, and subHooks (sub hooks called in this hook) insubHooks
.In fact in react-devtool they also get current hook state value with it after some tweaks.
use cases
Basically the use cases would be similar to the tests in
react-debug-tools
. The following use case is an example ofinspectHook()
in enzyme; For such simple test case the better way to test the counter may be justfind()
the<p>
and assert the count, though. :Note that not only
useState()
will be in the tree returned frominspectHook()
; All hooks called in the function component will be logged, if it's a hook with state thevalue
will be the state, or it would depends on what the hook is.limitation
With this way we can get the state of hook, but we cannot get the returned value of hook calls. So it's still not possible to get the
seState
returned fromuseState()
, ordispatch()
fromuseReducer()
, or returned value from custom hook in this way.Also I'm not sure if it's doable in
shallow()
since shallow renderer is not based on fiber.implementation
inspectHookOfFiber()
which we bring into enzyme and return the result.As for porting
inspectHookOfFiber()
, we have to get the hook dispatcher in react secret shared internals in enzyme. In react-devtool they have their own way to communicate with React (React in fact inject the secret internal into the react-devtool to make it work). I'm sure we can get the secret internal throughReact.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
. Though it works to use it directly, It's not a good idea to couple with that private internal. The alternative way may be add devtool hook in enzyme side and let the React inject the internal into enzyme. I'll try to look into this further more.setHookState()
The second is
setHookState()
-- an api to set the state of hook and trigger the rerender. LikeinspectHook()
there's also an internal function in React calledoverrideHookState
, which is injected into react-devtools here so you can edit the state value of hook in react-devtool. I'd also like to port this functionality into enzyme, too.use case
The usage of
overrideHookState
in React is like thisIn enzyme we could find the fiber and handle the state update path (the 3rd arg) by ourselves. So the api only need to receive 2 arguments:
id
for the hook id, which is included in the tree node returned frominspectHook
, andvalue
for the changed value:implementation
To port the
overrideHookState
it looks like the only way is to let React inject devtool internal into it. I think we could build a same global hook as react-devtool before import React, but I haven't tried it yet nor known if we can always ensure React injects internal after we install the hook.If it's doable then the remaining work is as same as
inspectHook
-- find the fiber node and calloverrideHookState
to get the result.Planning
If these sound good, I'd like to work on this with following steps:
inspectHookOfFiber
into enzyme, with hard-coding secret internal (i.e. directly useReact.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
), and prove that we are able to add.inspectHook()
in enzyme.setHookState()
should be relatively easy.Also note that https://github.com/facebook/react/pull/14906#issue-254828738 said that
react-debug-tools
should be released soon. Once it's released we can just useinspectHookOfFiber()
without porting (But might still need to build a devtool hook foroverrideHookState
). I'll also open an issue to request the releasing if new apis are desirable. It also might be good to start work after React releasing this.And I'm not sure if it's what enzyme maintainers / users want. So feedback are welcome :-)
Related old discussion / PR in React / react-devtool: https://github.com/facebook/react-devtools/pull/1272 https://github.com/facebook/react/pull/14906