admin管理员组

文章数量:1389805

I'm starting to get comfortable with Vue's composition API, but every time I make a composable I ask myself which pattern to use. If I have a composable whose state depends on outside triggers:

  • should I return a function from the composable that performs an action on the state? Or
  • should I instead let the composable accept a reactive state from outside to update the state managed by the composable?

Scenario: list users

For example, I need to make a composable that fetches a list of users from an API. The list must reactively update when filtered by user name.

Composable pattern 1: return reactive list and action on that list

<script setup lang="ts">
const { list, setList } = useUserList();
</script>

<template>
  <pre>{{ list }}</pre>
  <button type="button" @click="setList('john')">List users named "John"</button>
</template>

Composable pattern 2: return reactive list based on input reactive dependency

<script setup lang="ts">
const nameFilter = ref('');
const { list } = useUserList(nameFilter);
</script>

<template>
  <pre>{{ list }}</pre>
  <button type="button" @click="nameFilter = 'John'">List users named "John"</button>
</template>

Which of the two patterns should I do? The example scenario may be simple enough to make the choice negligible, but on more complex logic (e.g., making higher-order composables) I imagine its effect compounding.

I'm starting to get comfortable with Vue's composition API, but every time I make a composable I ask myself which pattern to use. If I have a composable whose state depends on outside triggers:

  • should I return a function from the composable that performs an action on the state? Or
  • should I instead let the composable accept a reactive state from outside to update the state managed by the composable?

Scenario: list users

For example, I need to make a composable that fetches a list of users from an API. The list must reactively update when filtered by user name.

Composable pattern 1: return reactive list and action on that list

<script setup lang="ts">
const { list, setList } = useUserList();
</script>

<template>
  <pre>{{ list }}</pre>
  <button type="button" @click="setList('john')">List users named "John"</button>
</template>

Composable pattern 2: return reactive list based on input reactive dependency

<script setup lang="ts">
const nameFilter = ref('');
const { list } = useUserList(nameFilter);
</script>

<template>
  <pre>{{ list }}</pre>
  <button type="button" @click="nameFilter = 'John'">List users named "John"</button>
</template>

Which of the two patterns should I do? The example scenario may be simple enough to make the choice negligible, but on more complex logic (e.g., making higher-order composables) I imagine its effect compounding.

Share Improve this question edited Mar 13 at 8:47 Christian asked Mar 13 at 8:38 ChristianChristian 8311 gold badge6 silver badges19 bronze badges 2
  • My perspective is that it depends on the scenario—just as there's no universal code, there's no universal design pattern. Focusing specifically on the two patterns you mentioned: Composable Pattern 1 is more cohesive, while Composable Pattern 2 is more flexible. If the code involving nameFilter becomes extensive, using Composable Pattern 1 would lead to overly bloated code, in which case I would opt for Composable Pattern 2 or other patterns. – yuanyxh Commented Mar 13 at 8:57
  • @yuanyxh interesting take. But I don't think code in pattern 1 would necessarily become bloated much more than pattern 2 in your scenario. I could also create a nameFilter ref in pattern 1 then call setList(nameFilter.value) every time the ref value changes. – Christian Commented Mar 13 at 9:45
Add a comment  | 

1 Answer 1

Reset to default 0

Setter function is generally redundant in Vue because refs are reactive and can be watched. As a rule of thumb, modified pattern 1 is commonly used:

const { list, filter } = useUserList();

If a composable is implemented for team or public usage, then both modified pattern 1 and pattern 2 are applicable. It may be necessary to pass a ref that already exists to a composable.

A universal implementation that allows both uses:

function useUserList(filter = ref()) {
  const list = ref();
  ...
  return { list: readonly(list), filter }
}

本文标签: vuejsExposing actions in composable vs accepting reactive state to update internal stateStack Overflow