My computed property is not updating (composables) - vue.js

I'm starting to use Vue 3 and composables. However, it seems that the computed properties that depends on variables (or computed properties) in other composables are not updating.
This is my first composable:
import { ref, computed } from 'vue'
export default function useSum1() {
const num1 = ref(2)
const num2 = ref(3)
// Simple sum
const sum1 = computed(() => num1.value + num2.value)
return {
num1,
num2,
sum1
}
}
My second composable:
import { ref, computed } from 'vue'
export default function useSum2 () {
const num3 = ref(4)
const num4 = ref(1)
const sum2 = computed(() => num3.value + num4.value)
return {
num3,
num4,
sum2,
}
}
And my third composable, this one requieres sum1 and sum2 from the previous composables:
import { computed } from 'vue'
import useSum1 from './useSum1'
import useSum2 from './useSum2'
const { sum1 } = useSum1()
const { sum2 } = useSum2()
export default function useSum3() {
// This doesn't update when sum1 or sum2 changes, but works the first time
const sum3 = computed(() => sum1.value + sum2.value)
return {
sum3
}
}
In App.vue:
import useSum1 from './composables/useSum1'
import useSum2 from './composables/useSum2'
import useSum3 from './composables/useSum3'
const { num1, num2 } = useSum1()
const { num3, num4 } = useSum2()
const { sum3 } = useSum3()
setTimeout(() => {
num1.value = 3
}, 2500)
setTimeout(() => {
num3.value = 5
}, 5000)
// sum3 is always 10, never updates
Thank you for any help.

Related

what is the difference between writing composables and exporting stuff separately?

We're developing an application on vue3-composition-api.
so we are developing scripts on this concept called composables. We use this composables in different places for reusing stuff instead of rewriting them, The problem is that some parts of the composables are created but not being used and it seems to have an impact on performance. Checkout these three approaches.
useFeatureA.js - FIRST APPROACH (using stateful composables)
export function useFeatureA() {
const x = ref(false)
const y = computed(() => {
// uses x.value
})
const z = computed(() => {
// uses x.value
})
const foo = () => {
// uses y.value and z.value
// sets on x.value
}
const bar = () => {
// uses z.value
// sets on x.value
}
return { y, foo, bar }
}
useFeatureA.js - SECOND APPROACH (exporting separately and recompute everytime)
export const getX = () => {
return ref(false);
}
export const getY = (x) => {
return computed(() => {
// uses x.value
});
}
export const foo = (xValue, yValue) => {
const z = // using xValue
// uses yValue and z
return // something to write on x
}
export const bar = (xValue) => {
const z = // using xValue
// uses and z
return // something to write on x
}
ComponentA
<script setup>
const { getX, getY, foo, bar } = useFeatureA();
const x = getX();
const y = getY(x);
x.value = foo(x.value, y.value);
x.value = bar(x.value);
</setup>
useFeatureA.js - THIRD APPROACH (move state to component)
export const getX = () => {
return ref(false);
}
export const getY = (x) => {
return computed(() => {
// uses x.value
});
}
export const getZ = (x) => {
return computed(() => {
// uses x.value
});
}
export const foo = (yValue, zValue) => {
// uses yValue and zValue
return // something to write on x
}
export const bar = (zValue) => {
// uses and z
return // something to write on x
}
ComponentA
<script setup>
const { getX, getY, foo, bar } = useFeatureA();
const x = getX();
const y = getY(x);
const z = getZ(x);
x.value = foo(y.value, z.value);
x.value = bar(z.value);
</setup>
we are wondering whether this solution can be efficient or not and what the differences between these two approaches are.
NOTE: We came up with the idea of separating those two functions in two different composables but the problem is that the project is too big and this approach makes way too many composables while foo and bar are very close to each other because of being related to a specific feature. so we guess this approach of separating them on different composables is out of question.
UPDATE: we have this idea that if we need stateful composable, the second approach is a bottleneck since if the components uses the composable, after the component is destroyed, the state remains in memory.
we guess in second approach tree-shaking is more efficient.

How to send a parameter to getters in vue?

I have a getter to check the product stock and amount of the cart. So here is my getters.js:
export const checkStock = (state, productID) => {
let stockAvailable = true;
state.cart.forEach(item => {
if(item.product.id == productID) {
if(item.product.attributes.stock <= item.amount){
stockAvailable = false;
}
}
})
return stockAvailable;
}
So as you see, I am sending productID to this function as a parameter. And then in the Product component, I am calling this function and I want to add productID to this function but I don't know-how.
checkStockAvailability(productId) {
return this.$store.getters.checkStock;
},
So how do you think I can add productID to my getters function?
You can use parameters in getters by returning a function that returns the results:
export const checkStock = (state) => (productId) => {
// call the checkStockAvailability here
let stockAvailable = true;
state.cart.forEach(item => {
if(item.product.id == productID) {
if(item.product.attributes.stock <= item.amount){
stockAvailable = false;
}
}
})
return stockAvailable;
}
// then you can use this getter on the component as
const productId = 2;
store.getters.checkStock(productId)

typeError object(...) is not a function

I get the "object is not a function" error on the line where the "setup()" function appears.
I have no clue how to debug this... Am I doing something wrong? (I'm not an experimented developer, so I know I'm doing things wrong, but here, I have no clue what it is...)
It's asking me to add more details because my post is mainly code... So I'm writing this, but I habe idea what details I could c=possibly add. :-)
Thanks in advance for your help!!
<script>
import { ref } from 'vue'
import Ftable from '#/components/tables/Ftable.vue'
import Searchbar from '#/components/tables/Searchbar.vue'
import getMainCollection from '#/composables/getMainCollection'
export default {
name: 'Home',
components: { Searchbar, Ftable },
setup(){ //error arrives on this line
const { getData, getMore } = getMainCollection()
const documents = ref('')
const lastVisible = ref('')
const type = ref('')
const country = ref('')
function newSearch() {
const {doc, last} = getData(type, country)
documents.value = doc
lastVisible.Value = last
}
function askForMore() {
const { doc, last } = getMore(type, country, lastVisible)
documents.value = doc
lastVisible.value = last
}
return { documents, askForMore, newSearch, askForMore }
}
}
</script>
import { ref } from 'vue'
import { projectFirestore } from '#/firebase/config'
const getMainCollection = () => {
const collectionGroupRef = projectFirestore.collectionGroup('users')
const lastVisible = ref('')
const documents = ('')
function getData(type, country) {
const filter = null
if (type != null && country == null){
filter = collectionGroupRef.where('type', '==', `${type}`)
}
else if(type == null && country != null){
filter = collectionGroupRef.where('country', '==', `${country}`)
}
else{
filter = collectionGroupRef
}
const data = filter.orderBy('createdAt')
.limit(2)
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
documents.value.push({ ...doc.data(), id: doc.id})
})
lastVisible = querySnapshot.docs[querySnapshot.docs.length-1]
})
return( documents, lastVisible)
}
function getMore(type, country, lastVisible) {
const filter = null
if (type != null && country == null){
filter = collectionGroupRef.where('type', '==', `${type}`)
}
else if(type == null && country != null){
filter = collectionGroupRef.where('country', '==', `${country}`)
}
else{
filter = collectionGroupRef
}
filter.startAfter(lastVisible)
.limit(2)
.orderBy(createdAt)
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
documents.value.push({ ...doc.data(), id: doc.id })
})
lastVisible = querySnapshot.docs[querySnapshot.docs.length-1]
})
return( documents, lastVisible)
}
return ( getData, getMore )
}
export { getMainCollection }
Since you exported getMainCollection as an object:
export { getMainConnection }
You need to destructure it when importing:
import { getMainCollection } from '#/composables/getMainCollection'

integrate vue-i18n in vue-beautiful-chat

I try to integrate (vue-i18n) at this library (https://github.com/mattmezza/vue-beautiful-chat) in src folder but I have some integration problems
so,
in this file ./i18n/translations.js => we have the translations
in src/index.js
import Launcher from './Launcher.vue'
import VTooltip from 'v-tooltip'
import VueI18n from 'vue-i18n'
import messages from './i18n/translations.js'
const defaultComponentName = 'beautiful-chat'
const Plugin = {
install (Vue, options = {}) {
/**
* Makes sure that plugin can be installed only once
*/
if (this.installed) {
return
}
Vue.use(VueI18n)
const locale = navigator.language
const i18n = new VueI18n({
fallbackLocale: 'fr',
locale: locale,
messages
})
this.installed = true
this.event = new Vue({i18n})
this.dynamicContainer = null
this.componentName = options.componentName || defaultComponentName
/**
* Plugin API
*/
Vue.prototype.$chat = {
_setDynamicContainer (dynamicContainer) {
Plugin.dynamicContainer = dynamicContainer
}
}
/**
* Sets custom component name (if provided)
*/
Vue.component(this.componentName, Launcher)
Vue.use(VTooltip)
Vue.use(VueI18n)
}
}
export default Plugin
And i start to change in the file "src/Launcher.vue" "you" in the header of the chat
computed: {
chatWindowTitle() {
if (this.title !== '') {
return this.title
}
if (this.participants.length === 0) {
return $t('participant.you_only')
} else if (this.participants.length > 1) {
return $t('participant.you_and_participants', { participant: 'this.participants[0].name' })
} else {
return 'You & ' + this.participants[0].name
}
}
},
but i receive this error
i have try few others methods as this.$i18n and others.
Can you help me please ?
Thanks a lot.
Are you possible missing the "this" when referring to "$t" on the computed property?
computed: {
chatWindowTitle() {
if (this.title !== '') {
return this.title
}
if (this.participants.length === 0) {
return this.$t('participant.you_only')
} else if (this.participants.length > 1) {
return this.$t('participant.you_and_participants', { participant: 'this.participants[0].name' })
} else {
return 'You & ' + this.participants[0].name
}
}
},

Dividing react-native Animated.Values

I want to produce an Animated.Value that is the result of dividing a constant by another Animated.Value.
const originalSize = 255;
const currentSize = this.props.someAnimatedValue;
// This method doesn't exist, but it's what I want:
const animatedScaleFactor = Animated.divide(originalSize, currentSize);
```
Since there is no Animated.divide() method, I only have at my disposal someAnimatedValue.interpolate(), which I can use to map an input range to an output range with a custom easing curve, Animated.multiply(), and Animated.modulus().
Any ideas?
UPDATE - use Animated.Divide instead.
Old answer:
Figured out how to get what I want f(x) = 1/x using an interpolator and custom easing function:
import React, {
Animated,
Component,
View
} from 'react-native';
export default class EasingTest extends Component {
state = {
input: new Animated.Value(0)
};
componentDidMount() {
const max = 937;
const output = this.state.input.interpolate({
inputRange: [0,max],
outputRange: [0,1/max],
easing: t => 1 / t
});
setInterval(() => {
const input = this.state.input.__getValue();
const expected = 1 / input;
const actual = output.__getValue();
const error = Math.abs(expected - actual) / max;
console.info(`f(${input}) = ${actual} (expected ${expected}, error ${Math.round(error * 100)}%`);
}, 200);
Animated.timing(this.state.input, {
toValue: max,
duration: 1000
}).start();
}
render() {
return <View />
}
}