"Got infinite loop even after applied conditional rendering" - infinite-loop

I've got this problem every time even after I've applied the condition or ternary operator. I don't know why. Please help me.[[I've figured that this problem is coming from this part. If I comment on this part then the error doesn't appear. But my point is I've applied the condition here](https://i.stack.imgur.com/Z5TGL.png)](https://i.stack.imgur.com/SL7F2.png)
I've applied the condition here
Then why it goes into an infinite loop?
const [bangla, setBangla] = useState(false);
const [splitText, setSplitText] = useState([]);
const { id } = useParams();
const { data: blogs = {}, refetch, isError, error, isLoading } = useQuery({
queryKey: ['blogs'],
queryFn: async () => {
const response = await fetch(`${process.env.REACT_APP_SERVER_URL}/blog/${id}`);
const data = await response.json();
return data;
}
})
if (isLoading) {
return (
<div>
<h1 className='text-xl font-semibold'>Loading...</h1>
</div>
)
}
if (isError) {
return (
<div>
<h1 className='text-xl font-semibold text-error'>Error : {error.message}</h1>
</div>
)
}
const { current, recent } = blogs;
const { _id, admin, like, comment, share, thumbnail, title, description, gallery, postingTime } = current;
if (description) {
if (description.includes(".")) {
// check if the blog description are in "English"
setSplitText(description?.split('.')); // split "description" into an array of strings
}
if (description.includes("।")) { // check if the blog description are in "Bangla"
setBangla(true);
setSplitText(description?.split('।')); // split "description" into an array of string
}
}

Related

how to implement useSWRinfinte with graphql query that has variables and using a cursor

i have the following useSWR hook to get the first 10 links after a cursor:
const AllLinksQuery = gql`
query allLinksQuery($first: Int, $after: String) {
links(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
edges {
cursor
node {
title
id
}
}
}
}
`;
const fetcher = (query, {first,after}) => request('/api/graphql', query, { first, after })
const AwesomeLinks = () => {
const { data, error, mutate } = useSWR( [AllLinksQuery, {first:10, after:null}] , fetcher , {
revalidateIfStale:true
} );
/** rest of the component ***/
}
its great for fetching the first 10 links, but i need to implement an infinite scroll feature, so useSWRinfinite looks like the logical thing to do. the problem is that i havent found anything in the SWR documentation on how to implement the useSWRinfinte hook with a graphql query that contains variables
with apolloClient, for example, i could just use the fetchMore function, like so:
<button
onClick={() => {
fetchMore({
variables: { after: endCursor },
updateQuery: (prevResult, { fetchMoreResult }) => {
fetchMoreResult.links.edges = [
...prevResult.links.edges,
...fetchMoreResult.links.edges,
];
return fetchMoreResult;
},
});
}}
>
im new to both graphql & SWR, so any attempt to figure it out by repurposing the answers to graphql in this discussion didnt work: https://github.com/vercel/swr/discussions/601
any tip would be appreciated
i found the solution:
const getKey = ( index, previousData ) => {
if( index === 0 ) return [AllLinksQuery, {first:3, after:null}]
return [AllLinksQuery, {first:3, after:previousData.links.pageInfo.endCursor}]
}
const fetcher = (query, {first,after}) => request('/api/graphql', query, { first, after })
const AwesomeLinks = () => {
const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite( getKey, fetcher, {
initialSize:1,
revalidateIfStale: true
} )
/** ** /
return (
/** **/
<button onClick={() => setSize(size + 1)}>fetch more</button>
/** **/
)
}
yup, that wasnt so complicated but of course i only figured it out after posting a question. hope this helps somebody!

how to update filtered data with RTK Query createSelector

I want to update todos with filtered data I created with createSelector. How to approach this situation to update effectively when new todo is added or deleted within component?
import { createSelector } from "#reduxjs/toolkit";
import {
useGetTodosQuery,
} from "../api/apiSlice";
export default function TodoList(){
// RTK Query filters
const selectCompletedPosts = useMemo(() => {
const emptyArray = [];
// Return a unique selector instance for this page so that
// the filtered results are correctly memoized
return createSelector(
(inputData) => inputData,
(data) =>
data?.data?.filter((todo) => todo.isCompleted === true) ?? emptyArray
);
}, []);
// Queries and Mutations
const {
data: todos,
completedTodos,
isLoading,
isSuccess,
isError,
error,
} = useGetTodosQuery(undefined, {
selectFromResult: (result) => ({
// We can optionally include the other metadata fields from the result here
...result,
// Include a field called `filteredData` in the result object,
// and memoize the calculation
completedTodos: selectCompletedPosts(result)
}),
});
const [deleteTodo, { isLoading: isDeleting }] = useDeleteTodoMutation();
let content;
if (isLoading) {
content = <div>loading..</div>;
} else if (isSuccess) {
content =
todos.length > 0 ? (
<div>
{todos.map((todo) => {
return <div>{todo.content} <span onClick={()=>deleteTodo({ id: todo.id })}></span></div>;
})}
</div>
) : (
<p>
No todos, yet.
</p>
);
} else if (isError) {
content = <p>{error}</p>;
}
return (
<div>{content}</div>
)
}

vue3 file upload get value undefined

I am trying to upload a file with vue, but the issue I am stuck with is this,
I can't access this.imageFile.value after selecting the photo.
The data I returned from setup but undefined.
Under normal conditions, imageFile.value works in setup.
<template>
<img v-show="imageUrl" :src="imageUrl" alt="">
<input #change="handleImageSelected,getdata" ref="fileInput" type="file" accept="image/*">
</template>
<script>
import {UseImageUpload} from "./use/UseImageUpload";
export default {
data() {
return {
Hello: null
}
},
methods: {
getdata() {
this.Hello = this.imageFile.value
}
},
setup() {
let {imageFile, imageUrl, handleImageSelected} = UseImageUpload();
return {
imageFile,
handleImageSelected,
imageUrl,
}
}
}
</script>
UseImageUpload.js
import {ref, watch} from "vue";
export function UseImageUpload() {
//image
let imageFile = ref("");
let imageUrl = ref("");
function handleImageSelected(event) {
if (event.target.files.length === 0) {
imageFile.value = "";
imageUrl.value = "";
return;
}
imageFile.value = event.target.files[0];
}
watch(imageFile, (imageFile) => {
if (!(imageFile instanceof File)) {
return;
}
let fileReader = new FileReader();
fileReader.readAsDataURL(imageFile);
fileReader.addEventListener("load", () => {
imageUrl.value = fileReader.result;
});
});
return {
imageFile,
imageUrl,
handleImageSelected,
};
}
First of all please try not to mix Options and Composition API - I know it might work but it is not necessary and in the most cases just an anti-pattern.
Composition API is there to replace the Options API or rather to give an alternative. They are just not supposed to work together or to be used together.
So this would improve your code:
setup() {
const Hello = ref(null);
const {imageFile, imageUrl, handleImageSelected} = UseImageUpload();
function getdata() {
Hello = imageFile.value
}
return {
imageFile,
handleImageSelected,
imageUrl,
getdata,
}
This should also fix your issue.

I want to remove a className after a few seconds, or re trigger the class animation when condition is met

Goal: When there is a price change,i want the price number to higlight for a few seconds with a color. I do that with the toogleClassName.
Problem: When the iteration is UP UP,or DOWN DOWN,the element already has that class and the CSS animation already ran.
JSX Code:
import React, { useEffect, useState, useRef } from "react";
import styles from "./CoinContainer.module.css";
function usePrevious(data) {
const ref = useRef();
useEffect(() => {
ref.current = data;
}, [data]);
return ref.current;
}
export default function CoinContainer({ coin, price }) {
const [priceUpdated, setPrice] = useState("");
const prevPrice = usePrevious(priceUpdated);
// Update price
useEffect(() => {
setInterval(priceUpdate, 20000);
}, []);
// Apply different flash-color style according to up$ or down$ from prev
const toggleClassName = () => {
return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
};
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result);
setPrice(result[key].usd);
});
}
return (
<div className={styles.padding}>
<h2>{coin}</h2>
{/* Here is the problem,i would like to remove the class after a few seconds,or edit the CSS code to retrigger the animation */}
<h3 className={toggleClassName()}>
{priceUpdated ? priceUpdated : price}$
</h3>
</div>
);
}
CSS Code
#keyframes upFadeBetween {
from {
color: green;
}
to {
color: white;
}
}
#keyframes downFadeBetween {
from {
color: red;
}
to {
color: white;
}
}
.redPrice {
animation: downFadeBetween 5s;
color: white;
}
.greenPrice {
animation: upFadeBetween 5s;
color: white;
}
Thanks so much for any feedback/help!
You could use another variable called e.g. reset
const [reset, setReset] = useState(false);
And then set this value at your methods using setTimeout
const toggleClassName = () => {
setTimeout(() => setReset(true), 6000);
return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
};
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result);
setReset(false);
setPrice(result[key].usd);
});
}
and finally
<h3 className={reset ? "" : toggleClassName()}>
you can do this
handleClick = event => event.target.classList.add('click-state');
or as react is js library so definitly it will support js DOM elements, well there is no doubt that it uses DOM also.
so you can do this also,
for adding class
document.getElementById("id here").classList.add("classname here");
for removing class
document.getElementById("id here").classList.remove("classname here");
OR
you can also use react States
You can use a variable that will be updated every time you trigger the fetch.
In this case, we will call it animationStyle
function CoinContainer({ coin, price }) {
const [priceUpdated, setPrice] = useState("")
const [animationStyle, setAnimationStyle] = useState("")
useEffect(() => {
setInterval(priceUpdate, 20000)
}, [])
function updateAnimationStyle(){
if (prevPrice > priceUpdated) {
setAnimationStyle("redPrice")
} else {
setAnimationStyle("greenPrice")
}
setTimeout(() => {
setAnimation("")
}, 5000)
}
function priceUpdate() {
return fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
)
.then((data) => data.json())
.then((result) => {
let key = Object.keys(result)
setPrice(result[key].usd)
updateAnimationStyle()
})
}
return (
<div className={styles.padding}>
<h2>{coin}</h2>
<h3 className={animationStyle}>//you avoid doing the conditional rendering that might be a bit confusing
{priceUpdated ? priceUpdated : price}$
</h3>
</div>
)
}
Okay, so basically, the approach is to avoid too large conditional rendering inside the jsx.
the function updateAnimationStyle gets called every time the fetch is triggered.
depending on the condition, updateAnimationStyle will set redPrice or greenPrice animations to animationStyle.
the animationStyle will clean after 5 seconds through setTimeout function. "it can not be less than 5s because you have set the animation duration to 5s".
const [priceUpdated, setPrice] = useState("") it is better to avoid this type of naming for variables.
Instead, if you use const [priceUpdated, setPriceUpdated] = useState(""), it will be more readable.
Hope it works for you!
Regards.

Display Redux-Form validation error for embedded component

I have embedded a React component inside of another where I have applied validation on the parent:
const EmailAddressInput = (props) => {
const { emailList, onKeyUp } = props;
return (
<div>
<textarea
{...emailList}
/>
</div>
);
};
It's placed inside another component like:
let Emailer = (props) => {
const { fields: { passType, invitees },
return (
<legend>Select pass type:</legend>
{ renderPassTypes(eventsState.selectedEvent.AssociatedPassTypes) }
{passType.touched && passType.error && <span className="error">{passType.error}</span>}
<EmailAddressInput { ...invitees } onKeyUp={ () => handleEmailToBarKeyUp(invitees.emailList.value) } />
{invitees.touched && invitees.error && <span className="error">{invitees.error}</span> }
)
}
Now, given I want to ensure the EmailAddressInput's emailList is not empty, I added a custom validation rule:
const emailValidator = createValidator({
invitees: [requiredProperty('emailList')],
passType: required,
});
My validation utility looks like:
export function required(value) {
if (isEmpty(value)) {
return 'Required';
}
}
export function requiredProperty(fieldName) {
return function (value) {
return required(value[fieldName]);
};
}
export function createValidator(rules) {
return (data = {}) => {
const errors = {};
Object.keys(rules).forEach((key) => {
const rule = join([].concat(rules[key])); // concat enables both functions and arrays of functions
const error = rule(data[key], data);
if (error) {
errors[key] = error;
}
});
return errors;
};
}
Now when I submit my form with the EmailAddressInput textarea empty, createValidator returns {invitees: 'Required'}. The form submission is halted as expected (hooray!) but the error message is lost.
Errors are added as an errors property of the Redux-Form field object, but invitees isn't a field object, so I guess for that reason the collection of errors isn't being attached.
The field is actually the emailList textarea in EmailAddressInput, but that isn't getting the errors collection attached as the relevant key in the errors collection is different (invitees vs emailList)
Any idea how I can get that error displayed?
The trick was to project the right structure from my validation rule functions:
export function requiredProperty(fieldName) {
return function (value) {
const error = required(value[fieldName]);
if (error) {
return { [fieldName]: error };
}
};
}

Resources