infinitered / reactotron

A desktop app for inspecting your React JS and React Native projects. macOS, Linux, and Windows.
https://docs.infinite.red/reactotron/
MIT License
14.89k stars 945 forks source link

Support for React Hooks #945

Open vfonic opened 5 years ago

vfonic commented 5 years ago

I just discovered this project. Great job! This looks super useful!

I'm trying to figure out if there's a way to track the "state"(s) of various React Hooks I'm using in my app?

I searched, but couldn't find any plugins.

Thanks!

rmevans9 commented 5 years ago

There is not currently a way to do this similar to how this is unable to track local state in class based components. I would be more then happy to extend reactotron to be able to work better with hooks but I haven't spent any time looking into how that might work. If there are ideas I am all ears

ianoshorty commented 5 years ago

I thought I'd spend an hour looking at this just to get back involved in the project. I couldn't come up with a solution straight away, but I did record a video of the session if you're interested (Though I've only just noticed the quality / size of text is terrible! lesson learned) - its at https://www.youtube.com/watch?v=ycOyEWZfkKA

The best I could come up with as a solution is to leverage a function currying based approach. Its not great, but it achieves what you're after.

I've created a sample project to demonstrate at https://github.com/ianoshorty/reactotron-hooks-example/ - see for example usage https://github.com/ianoshorty/reactotron-hooks-example/blob/master/src/App.js to

rmevans9 commented 5 years ago

Hey @ianoshorty! I am taking a look at the code but it looks like the video is blocked here in the states. I am assuming maybe due to some music?

vfonic commented 5 years ago

Same here. I assumed it's because I'm in Thailand and didn't have VPN available at the time to switch to US.

ianoshorty commented 5 years ago

Hey both - indeed it appears its been taken down due to the music coming out my alexa in the background! Oh well! Sorry about that. Hopefully you can still see the sample code though?

vfonic commented 5 years ago

@ianoshorty youtube 🤦‍♂️

brooksbecton commented 5 years ago

I can do this one! I have a similar logger that uses redux-logger with useReducer https://www.npmjs.com/package/useloggedreducer

I think this would be similar, but just use Reactotron.log

Any thoughts on what the API would look like? Would a developer just import this wrapper functions from Reactotron?

rmevans9 commented 5 years ago

Hey @brooksbecton!

I think it will be a little bit more in depth than just Reactotron.log because that will just put the information on the timeline. I would suspect we would want to wire it up closer to how Redux and MST work which allows you to view the state in the state tab as it changes. The API for such a thing would require sending a few different events throughout time:

state.action.complete - When an "Action" occurs. state.backup.response - When a backup is requested. This sends the current state. This one might be hard to handle in hooks I suspect. reactotron.stateValuesResponse - When state values are requested you have to respond with the values reactotron.stateValuesChange - When state values change you can push them up to reactotron with this.

I believe there would be a few more. One thing we could do is start out simple - get the ability to see hook values in the state tab then expand from there. As far as how the API to hook it up would look I think we have two real options:

For examples of what I would hope something like this would be capable of you can see https://github.com/infinitered/reactotron-mst/blob/master/src/reactotron-mst.ts

brooksbecton commented 5 years ago

I have a few things going here: https://github.com/brooksbecton/reactotron-react-hooks

I am using the useEffect to track state

  useEffect(() => {
    Reactotron.trackState({ count, name });
  }, [count, name]);

I able to see these values in Reactotron, but it doesn't currently respect any subscriptions.

What do you think about this approach?

rmevans9 commented 5 years ago

Interesting approach but the more I think about it the more I am thinking it might make sense to monkey patch useReducer or find how we can reach into the internals of React to hook into them because my concern with having to go out of your way to call trackState is people just won't do it. One of the beautiful things about the Redux and MST implementation is they are largely "set it and forget it"... until you want to look at state. If we are going to require the user to setup each and every state call then that might defeat the purpose.

brooksbecton commented 5 years ago

Yeah I think monkey patching is the only way you'll grab everything. I personally don't use the useReducer a whole lot, so I guess you would path useState too? That's the main reason I did the useEffect was so you could dump a lot of state in there to track it.

React state management hooks seem to be more single purpose than bigger state trees like Redux. I'm not familiar with MobX at all so can't speak to that.

rmevans9 commented 5 years ago

https://github.com/facebook/react/blob/42794557ca44a8c05c71aab698d44d1294236538/packages/react-dom/src/server/ReactPartialRendererHooks.js#L251

useState uses useReducer under the hood in react-dom. I suspect react-native is the same. Technically hooks can actually be a larger state tree like Redux/MobX with the use of useReducer. An idea someone threw out to me last week was if we can detect the component/function and then represent the different bits of the state in reactotron as just an array of values in the same order that they were registered. I would need to think through how to display that right but what if we tried to just track the component that is using the hook and monkey patching useReducer I think that would be a great start.

brooksbecton commented 5 years ago

Any advice on monkey patching or do you have any spots in the code that already do this?

rmevans9 commented 5 years ago

You should be able to just import it and set the import to a new function. I suspect that should do the trick. If it doesn't we will have to rethink it probably. If it doesn't work maybe we just export our own useState and useReducer that stubs out when Reactotron is not available.

Craigson commented 4 years ago

@brooksbecton this hook has been super helpful, thank you for posting it!

I've managed to successfully track my App's state using Reactron thanks to it. I'm not using typescript, so I refactored your hook ever-so-slightlty ( https://gist.github.com/Craigson/b6f6692153639bb187d52c7f43f518b7 ). I'm managing my state using a global context, wrapping each component in a Consumer and sit within the global context Provider. To track state, I'm simply doing so in my global reducer.

A very simplified example:

import Reactotron from "reactotron-react-native";

import actions from "./actions";
import { AsyncStorage } from "react-native";
import { UserModel } from "../models";

const initialState = {
  user: UserModel,
  ...
};

function reducer(state, { type, props, asyncAction }) {
  // shallow copy old state
  let newState = { ...state };

  // handle actions
  switch (type) {

    case actions.SET_USER:

      newState.user = {
        ...newState.user,
        ...props.user
      };
      break;
    }

  Reactotron.trackState({ state: newState });
  return newState;
}

export default reducer;
export { initialState };

Thanks again, super helpful!

ksielyov commented 2 years ago

ERROR TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[4], "./app/store/store").store')

Reactotron.trackState(rootReducer); Does not work