#Is it possible to useState to change class bindings with ref across components?

20 messages · Page 1 of 1 (latest)

placid venture
#

I am trying to get the click event on a button to toggle a class on an element of a sibling component. I have the function setup as a shared useState. However, it throws an error and says the toggle is not defined

#

Sorry been stuck on this one for a few days. Can't seem to work it out >_<

#

More detail:

// components/ComponentA.vue
<template>
    <div id="element"
    :class="{open: openComponent}">
    </div>
</template>

<script setup>
    const openComponent = ref(false)
</script>


// components/Component.B.vue
<template>
    <footer>
        <button 
        @click="openComponentToggle">
        </button>
    </footer>
</template>

<script setup>
    const openComponentToggle = useOpenComponentToggle();
</script>


// composables/states.js
export const useOpenComponentToggle = useState('openComponentToggle', () => openComponent.value = !openComponent.value);
#

This returns nuxt instance unavailable 😢

magic current
#

hi, you're getting nuxt instance unavailable because useOpenComponentToggle should be a function to ensure that the context is set when it is called.

// composables/states.js
export const useOpenComponentToggle = () => useState('openComponentToggle', () => openComponent.value = !openComponent.value);
placid venture
#

Hey @magic current appreciate the reply. That makes sense and I've tried implementing that. However, there seems to be one more issue:

#

Now it's saying "openComponent is not defined", referring to the 'states.js' file

#

I guess it's not able to find the element to target? not sure what I'm missing...

delicate aurora
#

openComponent is not reachable in your module. It's defined in a setup context.

Your composable should just define an initial value so the state can be set when initiated for the first time.

export const useOpenComponentToggle = () => useState('openComponentToggle', () => false)

In ComponentA.vue you'd then reference the state, which will be false by default:

<script setup>
    const openComponent = useOpenComponentToggle()
</script>

In ComponentB.vue, you'd toggle the value as per usual:

<script setup>
    const openComponentToggle = useOpenComponentToggle();
</script>

<template>
  <button @click="openComponentToggle = !openComponentToggle" />
</template>
#

Or go one step further and congregate into a re-usable API:

const useOpenComponentToggleState = () => useState('openComponentToggle', () => false)

export const useOpenComponentToggle = () => {
  const state = useOpenComponentToggleState()
  
  const toggle = () => state.value = !state.value

  return {
    state,
    toggle
  }
}

and use it in ComponentA.vue as such:

<script setup>
  const { state: isOpen } = useOpenComponentToggle()
</script>

<template>
  <div id="element" :class="{ open: isOpen }" />
</template>

and in ComponentB.vue as such:

<script setup>
  const { toggle } = useOpenComponentToggle()
</script>

<template>
  <button @click="toggle" />
</template>

the world is your oyster.

placid venture
#

Just working through this now. Appreciate it @delicate aurora !!

#

Just trying to get the first solution working before trying the re-usable API. The code runs but the class on the element doesn't seem to toggle

#

Does this look correct for ComponentA.vue:

<script setup>
const openComponent = useOpenComponentToggle();
</script>

<template>
  <div id="element"
  :class="{open: openComponent}">
  </div>
</template>
placid venture
#

Ohh nice. Ok going to cross-reference and make sure mine checks out. Thanks for this!

placid venture
delicate aurora
placid venture
#

Hmm nope it was the first one I saw on codesandbox

#

Seems not to be working on my local server either, which should be up to date. But I’ll have to double check the version

placid venture
#

Upgraded to 3.4.2 and it seems to work now. Thank you! @delicate aurora 🙏