Accessing Hooks state from another file?

Hey guys, yet ANOTHER question with React & Hooks. Here is my original code:

  const [contactMe, setContactMe] = useState(false)
  // Toggle contact form
  const toggleContactMe = () => {
    if (!contactMe) {
      document.getElementById('contact').scrollIntoView({ behavior: "smooth" })
      setContactMe(!contactMe)
    } else {
      setContactMe(contactMe)
    }
  }

That I am trying to now modularize in its own file, but in the React tools, it does not show contactMe as part of the global state.

New code:

// Hooks
import useContactState from '../hooks/useContactState'

const {contactMe, toggleContactMe} = useContactState(false)

../hooks/useContactState file:

import { useState } from 'react'

export default initialContactState => {
  const [contactMe, setContactMe] = useState(initialContactState)

  return {
    // Toggle contact form
    toggleContactMe: () => {
      if (!contactMe) {
        document.getElementById('contact').scrollIntoView({ behavior: "smooth" })
        setContactMe(!contactMe)
      } else {
        setContactMe(contactMe)
      }
    }
  }
}


Not sure I understand. I thought toggleContactMe is the only thing I would be returning.

No, you absolutely did, this works!

import { useState } from 'react'

export default initialContactState => {
  const [contactMe, setContactMe] = useState(initialContactState)

  return {
    contactMe,
    // Toggle contact form
    toggleContactMe: () => {
      if (!contactMe) {
        document.getElementById('contact').scrollIntoView({ behavior: "smooth" })
        setContactMe(!contactMe)
      } else {
        setContactMe(contactMe)
      }
    }
  }
}


Had to pass the state through as well :slight_smile:

EDIT: I am genuinely curious if this is okay as I can’t find the state yet in any component in the React Tools.

1 Like

Are you trying to use a hook outside of a component? Because that’s not the point of them. If you want state accessible globally, you define your hook somewhere high up the component tree in a component, then pass the values you want down. Using Context to store the useState values then using useContext to access the values in the components you want makes this easier if the pop drilling becomes onerous.

You should be able to just view the state by clicking on a component in the devtools, eg:

Screenshot 2020-02-04 at 01.25.39

But if you are calling it outside of a component, it won’t work properly, so would be why you can’t see it

ALSO if you are trying to write a custom hook, it should be useInitialContactState, not initialContactState


edit: What is your aim here, overall? You’re fumbling about with the technicalities of hooks, which is great, and you’re getting good, very specific advice, but maybe might be helpful to you from an advice PoV if we had a high-level view of what you’re trying to acheive and how you’re structuring stuff to do it

1 Like

I am currently trying to clean up the main component of this project: https://github.com/rstorms90/chizetteArt/tree/fix-CA-cleanup/src/components

This file: https://github.com/rstorms90/chizetteArt/blob/fix-CA-cleanup/src/components/chizette-art/ChizetteArt.jsx

…And okay, this is where useContext comes in, huh?

But it seems to be the way I have it coded, the state is popping up like this under ‘Anonymous’?

Ah, I see. I understand why you’re having an issue here.

This one component on its own is incredibly complicated for what it needs to do. It’s immediately obvious that you are doing way, way, way too much in it. It should do one thing, this is doing lots and lots and lots of things. Everything in useEffect is a side effect, and that useEffect block is enormous. You need to track the state in your head while you’re building this, and because the state can be modified in so many ways by those side effects, that’s not really possible. (You can have a seperate useEffect for every individual thing there btw, there is no penalty to doing that, and it means individual effects can be extracted out, but not sure that’ll help a lot here).

I’ll have a read through and try to unpick it a bit, but it’ll take me a while I think.

So

  • The aim is to show a gallery of images.
  • There is a nav bar, which allows you to filter the results.
  • The overall “page” has a header and footer which aren’t really relevant atm, I’ll ignore them for the minute.
  • The gallery “page” shouldn’t really care about login logic, it’s not relevant. It should only care whether a user is logged in or not.
  • That login logic can go at the very top level of the app, seperated. When that state changes, anything that relies on that will rerender, which is what you want. This is a good usecase for Context. It should not be tied to the fetching/flitering logic for the gallery. Because you’re doing everything in ChizetteArt.jsx, literally anything a user does will cause the entire app to rerender.
  • You can see the effect by using the Profiler tab in the React dev tools: record a session and interact with the app to get profiling graphs.
  • You are refetching the data every time a user filters, but you only need that data once, when the app loads (this is another good usecase for Context, but it should be a seperate context). It is, for normal usage, read-only.
  • There is situation where that last point is not true, and that is when an admin user uploads a new image. At that point the updated data should be refetched: that is the only point at which the refetch needs to occur, and it is not going to be a common occurance.
  • There are two very similar libraries for data fetching client-side that work really well that may be well worth looking at, providing a hook to make async calls: react-async and swr. The latter has a slightly nicer API & better docs (imo), and it’s supported by Zeit, who use it for the Next.js framework (that tbh you should also probably look at, as it’s for building sites like yours would likely drastically simplify a lot of your logic)
  • The filtering logic could literally be put in the components that render the gallery images, you’re overabstracting here - there are only three options.
    myGalleryData.filter(/* do filtery stuff */).map(/* map the images here */)
    
  • If the state is complicated, you have useReducer rather than lots and lots of useStates.

Thank you so much. Very humbling to see how much more I have to go on this. This is from old code being a year ago that I just switched over to hooks. My understanding a year ago was really, really bad, so I’m just trying to improve it. What would my best course of action here? Learn Next.js, then continue on? I thought you need a smart top level component generally being app, but I just made it ChizetteArt.jsx? I will take everything you said in stride and go through.

Also, I think a massive flaw of mine now is after hearing that we need stupid children components, somehow I took that as, “No logic in child components at all.”