javascript - How can recreate a React useState in Vue? - Stack Overflow

admin2025-04-17  3

I am trying to create a toggle functionality to make a search bar appear and reappear by pressing on a button. I am unsure to why this code is wrong. Does anyone have any insight?

<template>
  <button
    @click="toggleSearchBar"
    v-if="! searchBarOpen"
  >
    Open Search bar
  </button>
  <div
    v-else
  >
    <input />
    <button
      @click="toggleSearchBar"
    >
    </button>
  </div>
<template>

<script setup>
import { ref } from "vue";

let searchBarOpen = ref(false);

const toggleSearchBar = () => {
  if (searchBarOpen) {
    searchBarOpen = false;
  } else {
    searchBarOpen = true;
  }
  console.log({ searchBarOpen });
};
</script>

I am trying to create a toggle functionality to make a search bar appear and reappear by pressing on a button. I am unsure to why this code is wrong. Does anyone have any insight?

<template>
  <button
    @click="toggleSearchBar"
    v-if="! searchBarOpen"
  >
    Open Search bar
  </button>
  <div
    v-else
  >
    <input />
    <button
      @click="toggleSearchBar"
    >
    </button>
  </div>
<template>

<script setup>
import { ref } from "vue";

let searchBarOpen = ref(false);

const toggleSearchBar = () => {
  if (searchBarOpen) {
    searchBarOpen = false;
  } else {
    searchBarOpen = true;
  }
  console.log({ searchBarOpen });
};
</script>
Share Improve this question edited Mar 5 at 10:39 rozsazoltan 11.3k6 gold badges21 silver badges59 bronze badges asked Jan 31 at 19:51 youssefragyoussefrag 115 bronze badges 2
  • 2 "let" is a mistake, you shouldn't reassign ref value. You'd have the same problem with react ref too – Estus Flask Commented Jan 31 at 20:31
  • 2 Vue and React behave differently regarding their reactivity. Don't try to convert but rather spend some time reading the docs to understand how it is supposed to work. – kissu Commented Jan 31 at 21:51
Add a comment  | 

4 Answers 4

Reset to default 1

Although I wrote an answer based on the typo in the question, I will specifically answer the question now.

  • How to work Vue Reactivity? - StackOverflow

If we have understood Vue reactivity and still want to accomplish something interesting like this, let's continue.

The React useState hook returns two values: the readonly current state and a function to update the reactive stored variable.

const [state, setState] = useState(initialValue);

In Vue, we can create a function called useState, where we initialize a ref, return it as readonly, and define a setState function to modify the stored ref value.

import { ref, readonly } from 'vue';

function useState(initialValue) {
  const state = ref(initialValue);

  const setState = (newValue) => {
    if (typeof newValue === "function") {
      state.value = newValue(state.value);
    } else {
      state.value = newValue;
    }
  };

  return [readonly(state), setState];
}

This useState function provides a similar experience in Vue, allowing you to use it in the same way.

import { useState } from './useState.js';

const [count, setCount] = useState(0);

const increment = () => setCount((prev) => prev + 1);

Live Vue CDN Example

const { createApp, ref, readonly } = Vue;

// Custom useState function
function useState(initialValue) {
  const state = ref(initialValue);

  const setState = (newValue) => {
    if (typeof newValue === "function") {
      state.value = newValue(state.value);
    } else {
      state.value = newValue;
    }
  };

  return [readonly(state), setState];
}

// Vue app setup
const app = createApp({
  setup() {
    const [openStatus, setOpenStatus] = useState(false);
    const [count, setCount] = useState(0);

    const toggleOpenStatus = () => setOpenStatus((status) => ! status)
    const increment = () => setCount(prev => prev + 1);

    return { openStatus, toggleOpenStatus, count, increment };
  },
  template: `
    <div>
      <button v-if="! openStatus" @click="toggleOpenStatus">Open</button>
      <div v-else>
        <p>Count: {{ count }}</p>
        <button @click="increment">Increment</button>
        <button @click="toggleOpenStatus">Close</button>
      </div>
    </div>
  `,
});

app.mount("#app");
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

<div id="app"></div>

Here is a React example as well.

const { useState } = React;

function App() {
  const [openStatus, setOpenStatus] = useState(false);
  const [count, setCount] = useState(0);

  const toggleOpenStatus = () => setOpenStatus(prevStatus => ! prevStatus);
  const increment = () => setCount(prev => prev + 1);

  return (
    <div>
      {! openStatus ? (
        <button onClick={toggleOpenStatus}>Open</button>
      ) : (
        <div>
          <p>Count: {count}</p>
          <button onClick={increment}>Increment</button>
          <button onClick={toggleOpenStatus}>Close</button>
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>

You simply need to set

searchBarOpen.value = true

(or false)

The value property in Vue refs is needed for reactivity internally.

In templates however, your reference to searchBarOpen is correct without value, because the value property will be unwrapped under the hood.

React

Live example with React CDN

const { useState } = React;

function App() {
  const [searchBarOpen, setSearchBarOpen] = useState(false);

  return (
    <div>
      {!searchBarOpen ? (
        <button onClick={() => setSearchBarOpen(true)}>Open Search Bar</button>
      ) : (
        <div>
          <input />
          <button onClick={() => setSearchBarOpen(false)}>Close</button>
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Vue

In Vue, you can declare reactive objects using the ref() function. You can read or modify their data through the .value property.

  • Reactivity: ref() function - Vue 3 Docs
  • Reactivity: reactive() function - Vue 3 Docs
  • ref() vs reactive() in Vue 3 - StackOverflow
  • What is the difference between ref(), toRef() and toRefs()? - StackOverflow

YourComponent.vue (Composition API, Vue 3)

<template>
  <div>
    <button v-if="!searchBarOpen" @click="toggleSearchBar">
      Open Search Bar
    </button>
    <div v-else>
      <input />
      <button @click="toggleSearchBar">Close</button>
    </div>
  </div>
</template>

<script setup>
const searchBarOpen = ref(false);

const toggleSearchBar = () => {
  searchBarOpen.value = ! searchBarOpen.value;
};
</script>

Live example with Vue CDN

const { createApp, ref } = Vue;

createApp({
  setup() {
    const searchBarOpen = ref(false);

    const toggleSearchBar = () => {
      searchBarOpen.value = ! searchBarOpen.value;
    };

    return { searchBarOpen, toggleSearchBar };
  },
  template: `
    <div>
      <button v-if="!searchBarOpen" @click="toggleSearchBar">
        Open Search Bar
      </button>
      <div v-else>
        <input />
        <button @click="toggleSearchBar">Close</button>
      </div>
    </div>
  `
}).mount("#app");
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

<div id="app"></div>

  • Declare a ref called searchBarOpen.

  • Then onClick on button arrow function switch boolean state.

  • In mustache of name of button, add a tertiary condition depending on searchBarOpen.

  • Show input for searching if searchBarOpen is true.

That's all

<template>
  <button
    @click="() => searchBarOpen = !searchBarOpen"
  >
     {{ searchBarOpen ? 'Close search bar' : 'Open search bar }}
  </button>
  <input v-if="searchBarOpen"/>
<template>

<script setup>
import { ref } from "vue";

const searchBarOpen = ref(false);
</script>
转载请注明原文地址:http://anycun.com/QandA/1744848811a88471.html