Pinia action not uploading array of object state (Vue 3) - javascript

I am using Vue 3 and Pinia, and I have a store cards.js, like this one:
import { defineStore } from 'pinia'
import { useLanguageStore } from './language'
import axios from 'axios'
export const useCardsStore = defineStore({
id: 'cards',
state: () => ({
cards: []
}),
actions: {
async generateCards() {
const languageStore = useLanguageStore();
this.cards = (await axios({
method: "post",
url: "<endpoint>", // some endpoint
data: {
text: languageStore.sentence,
language: languageStore.language
}
})).data.map(token => ({
text: token.text,
pos: token.pos,
}));
console.log(this.cards) // see (*)
}
}
})
Then, inside a component, I use something like this:
<template>
...
<input v-model="sentence">
<button #click.prevent="generateCards()">Generate</button>
...
</template>
<script setup>
import { storeToRefs } from 'pinia'
import { useCardsStore } from '../stores/cards'
import { useLanguageStore } from '../stores/language'
const { cards } = storeToRefs(useCardsStore())
const { sentence } = storeToRefs(useLanguageStore())
const { generateCards } = useCardsStore()
</script>
However, even though when I click in the button, console output after mofifying this.cards (*) is:
Proxy {0: {…}, 1: {…}, 2: {…}, 3: {…}}
[[Handler]]: Object
[[Target]]: Array(4)
0: {text: 'esto', pos: 'PRON'}
1: {text: 'es', pos: 'VERB'}
length: 2
[[Prototype]]: Array(0)
[[IsRevoked]]: false
(Hence the array is being correctly generated) Inside of Vue devtools I can see the state cards has not changed (as well as I can see it in the application).
Any help would be appreciated. Thank you.
Note: I followed this tutorial.

The answer was provided by #Phil. In the docs there is a link about not destructuring the store. The correct way was to change
const { generateCards } = useCardsStore()
to
const store = useCardsStore()
and use store.generateCards instead of store in the template.

Related

Type error while fetching icons with Next.js icons - Typescript

Hello Everyone
I'm having a problem within my Typescript code, with my Next.js App.
i cannot find the right type to use with JSX rendering icons, that's fetched from database,
First part of code:
import * as bsIcons from 'react-icons/bs';
import * as faIcons from 'react-icons/fa';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { IconType } from 'react-icons/lib';
type option = {
id: number,
opt: string,
url: string,
logo: any,
sub: string[]
}
export const getStaticProps: GetStaticProps<{ options: option[] }> = async () => {
const res = await fetch('http://localhost:3000/api/menu')
const options: option[] = await res.json()
return {
props: {
options
}
}
}
here i had imported the icons libs from react-icons/bs & react-icons/fa then i defined the type of the response option
then i used the Nextjs getStaticProps function to import menu items from the api.
Second part of code:
<ul>
{ options.map(elm => {
let iconList = elm.logo.split('.')[0]
let menuIcon:IconType = elm.logo.split('.')[1]
let Logo: Function;
if(iconList == 'bsIcons'){
Logo = bsIcons[menuIcon]
} else {
Logo = faIcons[menuIcon]
}
return (
<Link key={elm.id} href={`${elm.url}`} style={{ textDecoration: 'none', color: 'powderblue' }}>
<li className={styles.option}>
<Logo style={{padding: '0 .5rem'}}/>
<p>{elm.opt}</p>
</li>
</Link>
)
})}
</ul>
in this part i'm trying to render the fetched data, according to my code, it's working perfectly, but at the VS Code it shows a typescript error at the if statement,
Error:
let menuIcon: IconType
Type 'IconType' cannot be used as an index type.ts(2538)
i don't have any clue of the type i must use at this part, so i imported the IconType from the icons lib it self, but still i cannot fix it.
is there any suggestions for this ?
a Part of fetched data:
{
id: 1,
opt: 'Dashboard',
url: '/dash',
logo: `faIcons.FaGripHorizontal`,
sub: []
},
{
id: 2,
opt: 'Content management',
url: '/content',
logo: `faIcons.FaWarehouse`,
sub: []
},
}

How to set reactive object in Vuejs 3

I have a reactive object in the global space:
let cart = Vue.reactive({
order: {
itemsCount: 0
},
items: []
});
It works great. When we change this cart in any way (for example cart.order.itemsCount += 1;) other UI parts get updated automatically.
But if we set it to a new object (let's say we have received a new order object from server) then it breaks and won't work anymore:
cart = serverCart
We even tried cart = Vue.reactive(serverCart) and it still does not cause other UI parts to re-render.
It's possible that we set every property manaully:
cart.order.itemsCount = serverCart.order.ItemsCount;
cart.items[0].productTitle = serverCart.order[0].productTitle;
...
But this is idiotic of course.
How can we re-set a reactive object entirely in Vue.js 3?
Update:
You can see this codesandbox in action.
For setting new values reactive objects in vue3, you need to use Object.assign function and you should not reassign it
Here is an example:
<template>
{{ reactiveTest }}
<button #click="change">Change</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
let reactiveTest = reactive({
a: 1,
b: 2,
});
function change() {
const serverReactive = {
a: 3,
b: 4,
};
// reactiveTest = serverReactive; Do not do this
Object.assign(reactiveTest, serverReactive)
}
return {
reactiveTest,
change,
};
},
};
</script>
You are trying to copy by reference, try to copy by value with spread operator ... :
const { reactive } = Vue
const app = Vue.createApp({
el: "#demo",
setup() {
let cart = reactive({
order: {
itemsCount: 0
},
items: []
});
const someCart = {order: {itemsCount: 3}, items: [{id: 1, name: "aaa"}, {id: 2, name: "bbb"}, {id: 3, name: "444"}]}
cart = reactive({...someCart})
return {cart }
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<p> {{ cart }}</p>
<label>change items count</label>
<input type="number" v-model="cart.order.itemsCount" />
</div>

Content not loading and logging with delay

I'm doing a React project for my bootcamp and I'm passing a details array as a descripcion props from my ItemDetailContainer component to my ItemDetail component. When I try to console.log my descripcion props in ItemDetail, it first logs an empty array and after a few seconds it logs the actual array with my product's details.
The problem is that the ItemDetails component is not rendering the data from the array but I can log its content.
I thought this was caused do to the setTimeOut function or UseEffect hook in my code but I still get this problem when I remove them
My ItemDetailContainer code:
import { useState, useEffect } from "react";
import Product from "../../product.json";
import ItemDetail from "./ItemDetail";
import { Link } from "react-router-dom";
const ItemDetailContainer = () => {
const [details, setDetails] = useState([]);
const getItem = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Product)
}, 2000);
});
}
useEffect(() => {
getItem().then(setDetails)
}, []);
return (
<>
<div className="producto-descripcion">
<ItemDetail descripcion={details}></ItemDetail>
</div>
<button class="back-shopping"><Link className="route-link" to="/category/:id">go back to shopping</Link></button>
</>
)
}
export default `ItemDetailContainer`
My ItemDetail code:
import { useParams } from "react-router"
const ItemDetail = ({descripcion}) => {
console.log(descripcion)
const { id } = useParams();
return (
<>
<div className="produc-desc" id={id}>
{descripcion.filter(desc => desc.id === id).map((desc, index) => (
<div key={index}className="full-card">
<h2>{desc.name}</h2>
<img src={desc.pictureURL} alt="unisex hoodie" id="picture-store"/>
<p>{desc.price}</p>
<h4 id="texto-descripcion">{desc.description}</h4>
</div>
))}
</div>
</>
)
}
export default ItemDetail
The output of the console.log in the ItemDetail component:
Array(0)
and then:
(4) [{…}, {…}, {…}, {…}]
0: {id: 1, name: 'PHONE CASE', stock: '17', price: '$9.99', pictureURL: 'https://www.montblanc.com/variants/images/19971654706771425/A/w2500.jpg', …}
1: {id: 2, name: 'PINK HOODIE', stock: '12', price: '$24.99', pictureURL: 'https://cdn.shopify.com/s/files/1/0040/6146/2626/p…87992ClLogoHoodiepinkfront_1200x.png?v=1628299298', …}
2: {id: 3, name: 'WHITE SHIRT', stock: '23', price: '$14.99', pictureURL: 'https://hips.hearstapps.com/vader-prod.s3.amazonaw…m/1623333444-61jgr5y0ibl-ac-ul1500-1623333437.jpg', …}
3: {id: 4, name: 'BLACK HOODIE', stock: '9', price: '$24.99', pictureURL: 'https://ninefoldpath.org/wp-content/uploads/2018/05/NINE-BEATS-Logo-hoodie-600x600.jpg', …}
length: 4
What am I missing? Let me know if I wasnt clear. Thanks in advance.
The first time react renders the details state is [] therefore the first time it renders you see Array(0) in the console, and right after you see the desired array values, that is normal and expected React behaviour.
What you can do is something like this:
const ItemDetail = ({descripcion}) => {
console.log(descripcion)
const { id } = useParams();
return (
{
(description.length > 0) ?
<div className="produc-desc" id={id}>
{descripcion.filter(desc => desc.id === id).map((desc, index) => (
<div key={index}className="full-card">
<h2>{desc.name}</h2>
<img src={desc.pictureURL} alt="unisex hoodie" id="picture-store"/>
<p>{desc.price}</p>
<h4 id="texto-descripcion">{desc.description}</h4>
</div>
))}
</div>
: <div>Getting data</div>
}
)
}
export default ItemDetail
So you render a different message, in this case "Getting data" and as soon as the state changes you render the desired JSX, hope that makes sense.
There are many different ways to tackle this, I'm just giving you a simple workaround, you may want to check useEffect that will help you take more control over your component render lifecycles.
Finally solved it, basically the filter was comparing the useParams id which is a string to an integer id. Solved with a toString() like this:
Before desc.id === id after desc.id.toString() === id

React js diff2html Cannot read property 'getJsonFromDiff' of undefined

Link: codesandbox
I'm having the following problem can anyone help me out?
Error:
Cannot read property 'getJsonFromDiff' of undefined
-> let outStr = Diff2Html.getJsonFromDiff(dd, {
CodeDiff.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { createPatch } from "diff";
import { Diff2Html } from "diff2html";
import { InlineMath } from "react-katex/dist/react-katex";
import "highlight.js/styles/googlecode.css";
import "diff2html/lib/diff2html";
function CodeDiff(props) {
const { oldStr, newStr, context, outputFormat } = props;
const createdHtml = (oldString, newString, context, outputFormat) => {
function hljs(html) {
return html.replace(
/<span class="d2h-code-line-ctn">(.+?)<\/span>/g,
'<span class="d2h-code-line-ctn"><code>$1</code></span>'
);
}
let args = [
"",
oldString || "",
newString || "",
"",
"",
{ context: context }
];
let dd = createPatch(...args);
let outStr = Diff2Html.getJsonFromDiff(dd, {
inputFormat: "diff",
outputFormat: outputFormat,
showFiles: false,
matching: "lines"
});
let html = Diff2Html.getPrettyHtml(outStr, {
inputFormat: "json",
outputFormat: outputFormat,
showFiles: false,
matching: "lines"
});
return hljs(html);
};
const html = () => createdHtml(oldStr, newStr, context, outputFormat);
return (
<div id="code-diff" dangerouslySetInnerHTML={{ __html: html() }}></div>
);
}
CodeDiff.propTypes = {
oldStr: PropTypes.string.isRequired,
newStr: PropTypes.string.isRequired,
context: PropTypes.number,
outputFormat: PropTypes.string
};
CodeDiff.defaultProps = {
oldStr: "",
newStr: "",
context: 5,
outputFormat: "line-by-line"
};
export default CodeDiff;
Well, "diff2html" library exposes only "html" and "parse" functions, so in order to use it from single object Diff2Html like you want you have to import it diffrently, like so:
import * as Diff2Html from "diff2html";
But then there are no such things there as getJsonFromDiff and getPrettyHtml
Not sure where you got those from, getJsonFromDiff is actually a test name in their github - https://github.com/rtfpessoa/diff2html/search?q=getJsonFromDiff
But it is not a function. And there is not at all such thing as getPrettyHtml
So i suppose you wanted to use parse (instead of getJsonFromDiff) and html (instead of getPrettyHtml) that way it works fine as far as i can tell - https://codesandbox.io/s/material-demo-forked-jhljq?file=/CodeDiff.js:114-153
I had the same question, I was able to get access to these functions like this:
import * as Diff2HtmlLib from 'diff2html';
Diff2HtmlLib.Diff2Html.getJsonFromDiff(diff, diffOptions)

React.js - Taking array data from an API and ammending a '#' to it to form a hex color

A little bit of background about my project - I'm trying to take some information from a color API that I found online and create a color of the day application with it. Basically, it uses React to get a hex value from the API, and then I want to set the background color of my webpage to that hex color. The only problem is, the hex value that I recieve from the API does not have a '#' in front of it. Since I'm new to Javascript and React, I'm trying to take the hex code given to me from the API, add a '#' to the beginning of it, and then set that value as my background color. I just am not very sure of the syntax since I'm pretty new to JS. It's not too much code and I'll also include a link to the API I'm using. Any help would be appreciated!
import React, { Component } from "react";
import "./App.css";
class App extends Component {
constructor() {
super();
this.state = {
items: [],
isLoaded: true
};
}
componentDidMount() {
fetch("http://www.colr.org/json/colors/random/7")
.then(res => res.json())
.then(res => {
this.setState({
isLoaded: true,
items: res.colors
});
});
}
render() {
var { items, isLoaded } = this.state;
var itemName = items.map(item => (
<div key={item.id}>{item.tags[0].name}</div>
));
var itemHex = items.map(item => <div key={item.id}>{item.hex}</div>);
if (!isLoaded) {
return (
<div>
<h1>Not Loaded!</h1>
</div>
);
} else {
return (
<section style={{ backgroundColor: { itemHex } }} className="App">
<h1>today's color of the day is: {itemName}</h1>
<h2>Hex:{itemHex}</h2>
</section>
);
}
}
}
export default App;
The API I'm using is: http://www.colr.org/json/colors/random/7
Basically, I'm not too sure on how to 1.) append a '#' to the variable 'itemHex' and 2.) properly format the 'style' variable in my <section>. If I could get some help with both of these things, that'd be awesome! Thanks!
This should work
colors.forEach(color => color.hex = '#' + color.hex)
Result using example provided in your link:
0: {timestamp: 1187574525, hex: "#bdd5e0", id: 43679, tags: Array(2)}
1: {timestamp: 1187574417, hex: "#af3f4a", id: 41216, tags: Array(2)}
2: {timestamp: 1187574348, hex: "#a56149", id: 12284, tags: Array(2)}
3: {timestamp: 1187574327, hex: "#a195b3", id: 39159, tags: Array(2)}
4: {timestamp: 0, hex: "#591d35", id: 5464, tags: Array(2)}
5: {timestamp: 1187574615, hex: "#c8d1b8", id: 8433, tags: Array(3)}
6: {timestamp: 1111913319, hex: "#819eca", id: 8406, tags: Array(2)}

Categories

Resources