How to prevent component re-render on switching browser tabs? - reactjs

In my Next.js app, the component is getting re-rendered when I change the browser tab and then get back to the tab in which the app is already opened. e.g. app is open tab 1 and when I switch to tab 2 and then come back to tab 1.
Actually, I have a page on which listing of records appears, so when I do local filter using text match it is working fine. But when I change the tab and get back to the app tab, it resets the listing again.
When I filter the location with text then it does the filter.
But when I switch the tab it resets the result.
I am using useSwr for data fetching and display listing. Here below is code of component:
import useSWR from 'swr'
import Link from 'next/link'
import Httpservice from '#/services/Httpservice'
import { useState, useEffect, useCallback } from 'react'
import NavBar from '#/components/NavBar'
import Alert from 'react-bootstrap/Alert'
import Router, { useRouter } from 'next/router'
import NoDataFound from '#/components/NoDataFound'
import nextConfig from 'next.config'
import { useTranslation, useLanguageQuery, LanguageSwitcher } from 'next-export-i18n'
export default function Locations({...props}) {
const router = useRouter()
const { t } = useTranslation()
const [queryLanguage] = useLanguageQuery()
const httpService = new Httpservice
const pageLimit = nextConfig.PAGE_LIMIT
const [loading,setLoading] = useState(true)
const [pageIndex, setPageIndex] = useState(1)
const [locations, setLocations] = useState([])
const [searchText, setSearchText] = useState('')
const [locationId, setLocationId] = useState(null)
const [isExpanding, setIsExpending] = useState(null)
const [loadMoreBtn, setLoadMoreBtn] = useState(true)
const [locationName, setLocationName] = useState(null)
const [errorMessage, setErrorMessage] = useState(null)
const [tempLocations, setTempLocations] = useState([])
const [deleteMessage, setDeleteMessage] = useState(null)
const [successMessage, setSuccessMessage] = useState(null)
const [displayConfirmationModal, setDisplayConfirmationModal] = useState(false)
const showDeleteModal = (locationName, locationId) => {
setLocationName(locationName)
setLocationId(locationId)
setSuccessMessage(null)
setErrorMessage(null)
setDeleteMessage(`Are you sure you want to delete the '${locationName}'?`)
setDisplayConfirmationModal(true)
}
const hideConfirmationModal = () => {
setDisplayConfirmationModal(false)
}
const locationsFetcher = async() => {
try{
await httpService.get(`/v1/locations?page=${pageIndex}&limit=${pageLimit}`).then((response) => {
if(response.status == 200 && response.data) {
let data = response.data.results
setLocations([...new Set([...locations,...data])])
setTempLocations([...new Set([...locations,...data])])
if(response.data.next == undefined && response.data.results.length == 0) {
setLoadMoreBtn(false)
}
setLoading(false)
setIsExpending(null)
return data
} else {
setLoading(false)
setIsExpending(null)
const error = new Error('An error occurred while fetching the data.')
error.info = response.json()
error.status = response.status
throw error
}
}).catch((error) => {
setLoading(false)
setIsExpending(null)
})
} catch (error) {
setLoading(false)
setIsExpending(null)
}
}
const {data, error} = useSWR(`/v1/locations?page=${pageIndex}&limit=${pageLimit}`, locationsFetcher,{
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
if (error.status === 404) return
if (retryCount >= 10) return
setTimeout(() => revalidate({ retryCount }), 5000)
}
})
const loadMore = () => {
setPageIndex(pageIndex + 1)
setIsExpending(true)
}
const handleSearch = (e) => {
let searchKey = e.target.value
setSearchText(e.target.value)
if(searchKey.length > 0) {
console.log(tempLocations)
let foundValue = tempLocations.filter(location => location.name.toLowerCase().includes(searchText.toLowerCase()))
if(foundValue) {
setLoadMoreBtn(false)
setLocations(foundValue)
} else {
setLoadMoreBtn(true)
setLocations(tempLocations)
}
} else {
setLoadMoreBtn(true)
setLocations(tempLocations)
}
}
return (
<>
<NavBar />
<div className="app-wrapper">
<div className="app-content pt-3 p-md-3 p-lg-4">
<div className="container-xl">
<div className="row gy-4 mb-2">
<div className="col-12 col-lg-8">
<h1 className="page-head-title"> {t('locations')} </h1>
</div>
</div>
<div className="summary_col">
<div className="row gy-4">
<div className="col-12 col-lg-12">
<div className="dotted float-end">
<a href="javascript:void(0)">
<img src="/images/icons/dotted.png" width="16" height="4" alt="" />
</a>
</div>
</div>
</div>
<div className="row gy-4 mt-2">
<div className="col-6 col-lg-3 col-md-4">
<div className="input-group search_col">
<div className="form-outline ">
<input type="search" className="form-control" placeholder={t('search')} value={searchText} onChange={handleSearch} />
</div>
<button type="button" className="btn">
<img src="/images/icons/search.png" width="19" height="19" alt="" />
</button>
</div>
</div>
<div className="col-6 col-lg-9 col-md-8 ">
<Link href={{ pathname: '/settings/locations/add', query: (router.query.lang) ? 'lang='+router.query.lang : null }}>
<a className="btn btn-primary float-end">{t('location_page.add_location')}</a>
</Link>
</div>
</div>
<div className="row gy-4 mt-2">
<div className="col-12 col-lg-12">
<div className="vehicles_col table-responsive">
<table className="table" width="100%" cellPadding="0" cellSpacing="0">
<thead>
<tr>
<th>{t('location_page.name')}</th>
<th>{t('location_page.company')}</th>
<th>{t('location_page.contact')}</th>
<th>{t('location_page.email')}</th>
<th>{t('location_page.phone')}</th>
<th>{t('location_page.address')}</th>
<th>{t('detail')}</th>
</tr>
</thead>
<tbody>
{error && <tr><td><p>{t('error_in_loading')}</p></td></tr>}
{(loading) ? <tr><td colSpan="6"><p>{t('loading')}</p></td></tr> :
(locations && locations.length > 0) ? (locations.map((location, index) => (
<tr index={index} key={index}>
<td>{location.name}</td>
<td>
<a href="javascript:void(0)">
{(location.links && location.links.Company) ? location.links.Company : '-'}
</a>
</td>
<td>{location.contact}</td>
<td>{location.email}</td>
<td>{location.phone}</td>
<td>
{(location.address1) ? location.address1 : ''}
{(location.address2) ? ','+location.address2 : ''}
{(location.address3) ? ','+location.address3 : ''}
<br />
{(location.city) ? location.city : ''}
{(location.state) ? ','+location.state : ''}
{(location.country) ? ','+location.country : ''}
{(location.zip) ? ','+location.zip : ''}
</td>
<td>
<Link href={{ pathname: '/settings/locations/edit/'+ location.UUID, query: (router.query.lang) ? 'lang='+router.query.lang : null }}>
{t('view')}
</Link>
</td>
</tr>
))) : (<tr><td><NoDataFound /></td></tr>)}
</tbody>
</table>
<div className="click_btn">
{(loadMoreBtn) ? (isExpanding) ? t('loading') : <a href="javascript:void(0)" onClick={() => loadMore()}>
<span>{t('expand_table')}</span>
</a> : t('no_more_data_avail')}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

By default useSWR will automatically revalidate data when you re-focus a page or switch between tabs. This is what's causing the re-renders.
You can disable this behaviour through the options object in your useSWR call, by setting the revalidateOnFocus field to false.
useSWR(`/v1/locations?page=${pageIndex}&limit=${pageLimit}`, locationsFetcher, {
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
if (error.status === 404) return
if (retryCount >= 10) return
setTimeout(() => revalidate({ retryCount }), 5000)
},
revalidateOnFocus: false
})
Alternatively, you can use useSWRImmutable (rather than useSWR) to disable all kinds of automatic revalidations done by SWR.
import useSWRImmutable from 'swr/immutable'
// ...
useSWRImmutable(key, fetcher, options)
Which is essentially the same as calling:
useSWR(key, fetcher, {
// other options here
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false
})

There are two debug points that i suggest to try first, although I believe the problem isn't caused by this component.
export default function Locations({...props}) {
console.log('Render')
and
const locationsFetcher = async() => {
console.log('Fetch')
The above are to confirm when switching tabs,
if the Locations component repaints
if the locationsFetcher has refired
The above questions will help you to dig further. My guts feeling is that you have another piece in your code that detects the tab switching, ex. listening to the page active or not. Because by default this Locations component shouldn't repaint by itself.

Related

I want my button to increase my products but I am getting two problems

The first problem is when I use:
const [visible, setVisible] = useState(4);
and put it in:
products.slice(0, visible)
It is showing me all the products rather than 4. This issue doesn't occur when I use it like this:
products.slice(0, 4)
The second problem is that I want my button "Load More" to increase value of Visible but it is not working.
Here is the complete code:
import React, {useEffect, useState} from 'react'
import './ForYou.css'
import ForYouItem from './ForYouItem'
export default function ForYou(props) {
const [products, setProducts] = useState([]);
const [visible, setVisible] = useState(4);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const showMoreItems = () => {
setVisible((prevValue) => prevValue + 4)
}
return (
<div>
<div className="ForYou-container">
<div className="heading">
<a href="#" className='Big-text'> {props.Bigheading}</a>
</div>
<div className="row ">
{products.slice(0, visible).map((product) => {
return(
<div className="col-md-3 my-2 Products">
<ForYouItem Title={product.title.slice(0,50)} Price={product.price} Imageurl = {product.image}/>
</div>
)
}
)}
<button className='Load-btn' onClick={showMoreItems}>Load More</button>
</div>
</div>
</div>
)
}
Code for child component:
import React from 'react'
import './ForYouItem.css'
export default function ForYouItem(props) {
return (
<div>
<a href="#">
<div class="card" >
<img src={props.Imageurl} class="card-img-top" alt="..."/>
<div class="card-body">
<h5 class="card-title"> {props.Title}... </h5>
<p class="card-text">Rs.{props.Price}</p>
Buy Now!
</div>
</div>
</a>
</div>
)
}
I have tried your code and make a few modifications since there was no code for the child component so I can make it work. However I did not find any errors, check this code to see if you get any ideas on how to implement the increasing of the items and check the ForYouItems component as there might be the issue since it renders ok with less data.
import React, {useEffect, useState} from 'react'
const ForYouItem = ({Title})=>{
return <>
<div>{Title}</div>
</>
}
export default function ForYou(props) {
const [products, setProducts] = useState([]);
const [visible, setVisible] = useState(4);
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const showMoreItems = () => {
setVisible((prevValue) => prevValue + 4)
}
return (
<div>
<div className="ForYou-container">
<div className="heading">
<a href="#" className='Big-text'>props.Bigheading</a>
</div>
<div className="row ">
{products.slice(0, visible).map((product) => {
return(
<div className="col-md-3 my-2 Products">
<ForYouItem Title={product.title.slice(0,50)} />
</div>
)
}
)}
{
visible <= products.length-1 &&
<button className='Load-btn' onClick={showMoreItems}>Load More</button>
}
</div>
</div>
</div>
)
}

How to dynamically fetch api route from component in page folder?

i have a reusable contact form that works perfectly when used in the index.js file.
However when i use it from a component in the page folder i am having a 404 not found error message because it uses this route 3000/ourServices/conciergerie/api/contact/ instead of 3000/api/contact.
How do i ensure the it will always fetch the correct route? please see how i fetch the api below :
async function handleSubmit() {
const data = {
firstName,
email,
phone,
message,
};
const res = await fetch("api/contact", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data: data,
token: "test",
}),
});
alert("Message sent! Thank you\nWe will be in touch with you soon!");
}
pages/ourServices/conciergerie
import Image from "next/image";
import { AiOutlinePlus, AiOutlineMinus } from "react-icons/ai";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { Contact } from "../../components/contact/Contact";
import es from "../../locales/es-ES/conciergerie.json";
import en from "../../locales/en-US/conciergerie.json";
import Icon1 from "/public/1.svg";
import Icon2 from "/public/2.svg";
import Icon3 from "/public/3.svg";
const Conciergerie = () => {
let { locale } = useRouter();
let t = locale === "es-ES" ? es : en;
// const { t } = useTranslation(locale, "conciergerie");
let myIcons = [Icon1, Icon2, Icon3];
const scrollToConciergerie = () => {
window.scrollTo({
top: 300,
behavior: "smooth",
});
};
const myLoader = ({ src, width, quality }) => {
return `${src}?w=${width}&q=${quality || 75}`;
};
const [showform, setshowform] = useState(false);
useEffect(() => {
window.addEventListener("load", scrollToConciergerie);
return () => {
window.removeEventListener("load", scrollToConciergerie);
};
});
const showContactForm = () => {
return <Contact />;
};
const contentData = t.conciergerieData;
return (
<div className="section" onLoad={scrollToConciergerie}>
<div className="container">
<div className="text-center">
<h1 className=" my-4 text-capitalize" id="conciergerie">
{t.conciergerieHeader}
</h1>
</div>
<h3 className="text-capitalize concierge-subheading mt-3">
{t.conciergerieTitle}
</h3>
<p className="lead concierge-subheading-text">{t.conciergerieText}</p>
</div>
<div className="container">
<div className="row text-center mt-5">
{contentData?.map((item, index) => {
return (
<div className="col-md-4" key={index}>
<span className="fa-stack fa-4x">
<Image
layout="responsive"
src={myIcons[index]}
alt="icons"
className="svg-inline--fa fa-solid fa-stack-1x fa-inverse img-fluid"
aria-hidden="true"
focusable="false"
data-prefix="fas"
data-icon="house"
role="img"
objectFit="cover"
height={300}
width={300}
//loader={myLoader}
/>
</span>
<h4 className="my-3 text-hogar2 text-uppercase">
{item.title}
</h4>
<ul>
{item.text.map((text) => {
return (
<li key={text.id} className="list-unstyled">
<p className="m-0 text-muted text-list">
{text.content}
</p>
</li>
);
})}
</ul>
{item.id === "algomas" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
{item.id === "else" &&
(!showform ? (
<AiOutlinePlus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
) : (
<AiOutlineMinus
role="button"
onClick={() => {
setshowform(!showform);
}}
className="fs-2"
fill="#5ab4ab"
/>
))}
</div>
);
})}
</div>
{showform && showContactForm()}
</div>
</div>
);
};
export default Conciergerie;
can someone help me please?
The reason this problem is happening has to do with absolute and relative paths.
fetch("api/contact")
Is a relative path. The fetch function figures out the path of the current file, ie 3000/ourServices/conciergerie, and adds api/contact to it
On the other hand, if you add a "/" before the path :
fetch("/api/contact")
Fetch figures out the root path of the project, then adds the path you added, ie :
3000/api/contact
TL;DR: Change fetch("api/contact") to fetch("/api/contact").

React question : how to pass an id from a component to another on click?

I need your help with an app that I am building. It has a forum page and I have some issues with the forum and post components.
I am trying to pass the id of the post that the user clicked on, with history.push so on the post page the id in the url that I try to get with useParams, has the value of the one I send with history.push. The purpose is for some queries I do so I show the post with its comments.
For now the layout isn’t great because I have to make this feature work.
I do not understand why it doesn’t. My console.logs show null or undefined which make no sense to me.
Thank you if you can help me with this.
Here you have two routes present in the App component. It is important for the last route, the Post one were I use :id so I can get it with useParams.
{/* Route for Trainings Wakeup Rebirth */}
<Route path='#forum' exact component={TrainingsWakeupRebirth} />
<Route path='#forum/:id' exact component={Post} />
Here you have the entire code of the Forum page. Like that you can see how I use history.push to send the value.id of the post to the Post component and the way the component itself is built.
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import ReactPaginate from "react-paginate";
import Post from "../Post/Post";
import './TrainingsWakeupRebirth.scss';
import axios from "axios";
const TrainingsWakeupRebirth = (props) => {
let history = useHistory();
// const forumSectionRef = useRef();
// const postSectionRef = useRef();
const forumSection = document.getElementById('forum-block-wrapper');
const postSection = document.getElementById('post-section');
const showPost = () => {
if (forumSection.style.display === 'block') {
return forumSection.style.display = 'none',
postSection.style.display = 'block';
} else {
return forumSection.style.display = 'block',
postSection.style.display = 'none';
}
}
const [listOfPosts, setListsOfPosts] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const postsPerPage = 2;
const pagesVisited = pageNumber * postsPerPage;
const displayPosts = listOfPosts.slice(pagesVisited, pagesVisited + postsPerPage).map((value, key) => {
const forParams = () => {
return history.push(`#forum/${value.id}`);
}
const executeAll = () => {
forParams();
showPost();
if(forParams()) {
let id = value.id;
return id;
}
}
return (
<div key={key}>
<div className="topic-row" onClick={() => {executeAll()}}>
<div className="topic-title">{value.title}</div>
<div className="topic-image">
<img src={value.image} alt=""></img>
</div>
<div className="topic-message">{value.postText}</div>
</div>
</div>
);
});
const pageCount = Math.ceil(listOfPosts.length / postsPerPage);
const changePage = ({selected}) => {
setPageNumber(selected);
};
useEffect(() => {
axios.get("http://localhost:3001/posts").then((response) => {
setListsOfPosts(response.data);
});
}, []);
console.log(listOfPosts);
return (
<div className="forum" id="forum">
<div className="forum-section-wrapper page" id="forum-wrapper">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<div className="title">
<h1><span className="first-title-part">Krishna</span><span className="second-title-part">Hara</span></h1>
</div>
<div className="quote">
<span className="quote-left">FORUM</span><span className="quote-right">Eco Village</span>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<div className="forum-block-wrapper" id="forum-block-wrapper">
{displayPosts}
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
activeClassName={"paginationActive"}
/>
</div>
</div>
</div>
</div>
</div>
<div className="post-section" id="post-section">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<Post />
</div>
</div>
</div>
</div>
</div>
)
};
export default TrainingsWakeupRebirth;
Here is some code from the Post component, so you can see the code that should work but doesn't. Also the console.log(id)
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
const Post = (props) => {
let { id } = useParams();
const [postObject, setPostObject] = useState({});
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
console.log(id);
useEffect(() => {
axios.get(`http://localhost:3001/posts/byId/${id}`).then((response) => {
console.log(response);
setPostObject(response.data);
});
axios.get(`http://localhost:3001/comments/${id}`).then((response) => {
setComments(response.data);
});
}, [id]);
const addComment = () => {
axios.post("http://localhost:3001/comments", {
commentBody: newComment,
Postid: id,
})
.then((response) => {
const commentToAdd = { commentBody: newComment };
setComments([...comments, commentToAdd]);
setNewComment("");
});
};
console.log(postObject);
return (
<div className="post-section-wrapper">
{/* <div>
<div className="title">
{postObject.title}
</div>
<div className="image">
<img src={postObject.image}></img>
</div>
<div className="message">
{postObject.postText}
</div>
</div> */}
<div className="comments-wrapper">
<div className="">
<input
type="text"
placeholder="Comment..."
autoComplete="off"
value={newComment}
onChange={(event) => {
setNewComment(event.target.value);
}}
/>
<button onClick={addComment}> Add Comment</button>
</div>
<div className="comments-row">
{comments.map((comment) =>
(
<div key={comment.id} className="comment">
{comment.commentBody}
</div>
)
)}
</div>
</div>
</div>
);
}
export default Post;
Thank you very very much!!!
#DrewReese and #JoelHager Thank you so much for checking my code and for your advice. In the meantime I found out that we can pass to a component, aside from the pathname, other values with history.push that we retrieve by using useLocation in the component that we want to. I will answer my own question and add the code.
Here is my Forum component, I prefer adding the entire code so everything is clear. In forParams you will see how I pass the value that I need with useHistory and the attribute state and detail.
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import ReactPaginate from 'react-paginate';
import Post from '../Post/Post';
import './TrainingsWakeupRebirth.scss';
import axios from 'axios';
const TrainingsWakeupRebirth = (props) => {
let history = useHistory();
// const forumSectionRef = useRef();
// const postSectionRef = useRef();
const forumSection = document.getElementById('forum-block-wrapper');
const postSection = document.getElementById('post-section');
const showPost = () => {
if (forumSection.style.display === 'block') {
return forumSection.style.display = 'none',
postSection.style.display = 'block';
} else {
return forumSection.style.display = 'block',
postSection.style.display = 'none';
}
}
const [listOfPosts, setListsOfPosts] = useState([]);
const [pageNumber, setPageNumber] = useState(0);
const postsPerPage = 2;
const pagesVisited = pageNumber * postsPerPage;
const displayPosts = listOfPosts.slice(pagesVisited, pagesVisited + postsPerPage).map((value, key) => {
const forParams = () => {
history.push(
{
pathname: `#forum#${value.id}`,
state: { detail: value.id }
}
);
}
const executeAll = () => {
forParams();
showPost();
}
return (
<div key={key} onClick={() => {executeAll()}}>
<div className="topic-row">
<div className="topic-title">{value.title}</div>
<div className="topic-image">
<img src={value.image} alt=""></img>
</div>
<div className="topic-message">{value.postText}</div>
</div>
</div>
);
});
const pageCount = Math.ceil(listOfPosts.length / postsPerPage);
const changePage = ({selected}) => {
setPageNumber(selected);
};
useEffect(() => {
axios.get("http://localhost:3001/posts").then((response) => {
setListsOfPosts(response.data);
});
}, []);
console.log(listOfPosts);
return (
<div className="forum" id="forum">
<div className="forum-section-wrapper page" id="forum-wrapper">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<div className="title">
<h1><span className="first-title-part">Krishna</span><span className="second-title-part">Hara</span></h1>
</div>
<div className="quote">
<span className="quote-left">FORUM</span><span className="quote-right">Eco Village</span>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<div className="forum-block-wrapper" id="forum-block-wrapper">
{displayPosts}
<ReactPaginate
previousLabel={"Previous"}
nextLabel={"Next"}
pageCount={pageCount}
onPageChange={changePage}
containerClassName={"paginationBttns"}
previousLinkClassName={"previousBttn"}
nextLinkClassName={"nextBttn"}
activeClassName={"paginationActive"}
/>
</div>
</div>
</div>
</div>
</div>
<div className="post-section" id="post-section">
<div className="fluid-grid">
<div className="row">
<div className="col-12">
<Post />
</div>
</div>
</div>
</div>
</div>
)
};
export default TrainingsWakeupRebirth;
In the Post component with useLocation and useEffect I get location.state.detail which is the id of the Post, that with useState I set to the constant postId.
import React, { useEffect, useState } from "react";
import { useParams, useHistory, useLocation } from "react-router-dom";
import axios from "axios";
import './Post.scss';
const Post = (props) => {
// let { id } = useParams();
const location = useLocation();
const [postId, setPostId] = useState();
useEffect(() => {
console.log(location.pathname); // result: '#id'
if(location.state) {
console.log(location.state.detail); // result: postId
setPostId(location.state.detail);
}
}, [location]);
const [postObject, setPostObject] = useState({});
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
// console.log(id);
useEffect(() => {
axios.get(`http://localhost:3001/posts/byId/${postId}`).then((response) => {
console.log(response.data);
setPostObject(response.data);
});
axios.get(`http://localhost:3001/comments/${postId}`).then((response) => {
setComments(response.data);
});
}, [postId]);
const addComment = () => {
axios.post("http://localhost:3001/comments", {
commentBody: newComment,
Postid: postId,
})
.then((response) => {
const commentToAdd = { commentBody: newComment };
setComments([...comments, commentToAdd]);
setNewComment("");
});
};
if(postObject !== null) {
console.log(postObject);
}
return (
<div className="post-section-wrapper">
{postObject !== null
?
<div className="posts-wrapper">
<div className="title">
{postObject.title}
</div>
<div className="image">
<img src={postObject.image}></img>
</div>
<div className="message">
{postObject.postText}
</div>
</div>
:
null
}
<div className="comments-wrapper">
<div className="">
<input
type="text"
placeholder="Comment..."
autoComplete="off"
value={newComment}
onChange={(event) => {
setNewComment(event.target.value);
}}
/>
<button onClick={addComment}> Add Comment</button>
</div>
<div className="comments-row">
{comments.map((comment) =>
(
<div key={comment.id} className="comment">
{comment.commentBody}
</div>
)
)}
</div>
</div>
</div>
);
}
export default Post;

Problem in implementing file Upload in Reactjs

I am Implementing a file upload feature to get resume of job applicants in my Reactjs form.
Now whenever I click on Upload everything works fine but while the file is uploading browser throws an error.
Here is my fileUpload.js.
import React, { useState, useRef } from "react";
import axios, { CancelToken, isCancel } from "axios";
import { LinearProgressWithLabel } from "./ProgressBar";
const FileUpload = () => {
const [uploadPercentage, setUploadPercentage] = useState(0);
const cancelFileUpload = useRef(null);
const uploadFile = ({ target: { files } }) => {
let data = new FormData();
data.append("file", files[0]);
const options = {
onUploadProgress: progressEvent => {
const { loaded, total } = progressEvent;
let percent = Math.floor((loaded * 100) / total);
if (percent < 100) {
setUploadPercentage(percent);
}
},
cancelToken: new CancelToken(
cancel => (cancelFileUpload.current = cancel)
)
};
const BASE_URL = "https://api.quantel.in"
axios
.post(
`${BASE_URL}/api/v1/jobs/resume`,
data,
options
)
.then(res => {
console.log(res);
setUploadPercentage(100);
setTimeout(() => {
setUploadPercentage(0);
}, 1000);
})
.catch(err => {
console.log(err);
if (isCancel(err)) {
alert(err.message);
}
setUploadPercentage(0);
});
};
const cancelUpload = () => {
if (cancelFileUpload.current)
cancelFileUpload.current("User has canceled the file upload.");
};
return (
<>
<p>
<input
type="file"
className="form-control-file"
onChange={uploadFile}
/>
</p>
{uploadPercentage > 0 && (
<div className="row mt-3">
<div className="col pt-1">
<LinearProgressWithLabel value={uploadPercentage} />
</div>
<div className="col-auto">
<span
className="text-primary cursor-pointer"
onClick={() => cancelUpload()}
>
Cancel
</span>
</div>
</div>
)}
</>
);
};
export default FileUpload;
When I click on the browse button the browser throws the following error. And I am confused why is it so?
When you check for uploadPercentage > 0 change that to this
{uploadPercentage > 0 ? (
<div className="row mt-3">
<div className="col pt-1">
<LinearProgressWithLabel value={uploadPercentage} />
</div>
<div className="col-auto">
<span
className="text-primary cursor-pointer"
onClick={() => cancelUpload()}
>
Cancel
</span>
</div>
</div>
) : null }
All your code inside { ... } is treated as a function (inside JSX) and in your case when uploadPercentage === 0 it is returning undefined.

CoreUI Make API Calls

I have created some basic beginner React apps. But now I want to try using a template "core-ui".
https://github.com/coreui/coreui-free-react-admin-template
I would like to make requests to some external endpoints and retrieve some data but I'm not sure where to do it.
Here's what I've done on my own:
import React from 'react';
import RowCreator from './RowCreator';
class DisplayCountries extends React.Component {
constructor(props){
super(props);
this.state = {countries:[],
countriesClone:[]
};
}
componentDidMount() {
const axios = require('axios');
const url = 'http://localhost:8080/demo/api/countries';
axios.get(url).then(res=>{
console.log(res.data);
this.setState({countries:res.data,
countriesClone:res.data});
}).catch(error=>{
console.error('Error', error);
})
}
handleOnChange(event){
var filteredString = event.target.value;
var filteredCountries = [];
for(var country of this.state.countries){
if(country.cioc.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.name.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.capital.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.region.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ||
country.subregion.toLowerCase().indexOf(filteredString.toLowerCase())>=0 ){
filteredCountries.push(country);
}
}
this.setState({countriesClone:filteredCountries});
}
render(){
return (<div>
<div className="headerBox">
<div className="row">
<div className="col-sm-12 text-center">
<h1>Search Countries</h1>
</div>
</div>
<div className="row">
<div className="col-sm-12 text-center">
<h3>Demo to filter the list of countries</h3><br/>
</div>
</div>
</div>
<div className="searchBox">
<div className="row text-right">
<div className="col-sm-3"/>
<div className="col-sm-6 text-center">
<br/><input type="text" className="form-control input-lg" placeholder="Search any field by name" onChange={this.handleOnChange.bind(this)}/><br/>
</div>
<div className="col-sm-3"/>
</div>
</div>
<div className="container">
<div className="row">
<div className="col-sm-12"><br/>
<table className="table table-striped table-bordered">
<thead>
<tr>
<th>CIOC</th>
<th>Country</th>
<th>Capital</th>
<th>Region</th>
<th>Sub Region</th>
</tr>
</thead>
<tbody>
{this.state.countriesClone.map(country => <RowCreator item={country} key={country.cioc}/>)}
</tbody>
</table>
</div>
</div>
</div>
</div>
)}
}
export default DisplayCountries;
But when I review the view of the CoreUI page, I can't figure out where to add my constructor, etc. Any ideas
Here's an example of a .js file for one of the pages:
import React, { useState, useEffect } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import {
CBadge,
CCard,
CCardBody,
CCardHeader,
CCol,
CDataTable,
CRow,
CPagination
} from '#coreui/react'
import processesData from './ProcessData'
const getBadge = status => {
switch (status) {
case 'Active': return 'success'
case 'Inactive': return 'secondary'
case 'Pending': return 'warning'
case 'Banned': return 'danger'
default: return 'primary'
}
}
const Processes = () => {
const history = useHistory()
const queryPage = useLocation().search.match(/page=([0-9]+)/, '')
const currentPage = Number(queryPage && queryPage[1] ? queryPage[1] : 1)
const [page, setPage] = useState(currentPage)
const pageChange = newPage => {
currentPage !== newPage && history.push(`/processes?page=${newPage}`)
}
useEffect(() => {
currentPage !== page && setPage(currentPage)
}, [currentPage, page])
return (
<CRow>
<CCol xl={12}>
<CCard>
<CCardHeader>
<h4 id="process" className="card-title mb-0">Processes</h4>
</CCardHeader>
<CCardBody>
<CDataTable
items={processesData}
fields={[
{ key: 'id', _classes: 'font-weight-bold' },
'name', 'startDate', 'endDate'
]}
columnFilter
tableFilter
hover
sorter
striped
itemsPerPageSelect
itemsPerPage={5}
activePage={page}
clickableRows
onRowClick={(item) => history.push(`/process/${item.id}`)}
/>
</CCardBody>
</CCard>
</CCol>
</CRow>
)
}
export default Processes
I think you're getting confused because in core-ui page, a functional component is written which uses hooks. To read more about hooks, Please go through the official docs if you've not. https://reactjs.org/docs/hooks-intro.html
useState
useEffect
You can convert your class component into a functional component like this
const DisplayCountries = () => {
[countries, setCountries] = useState([]);
useEffect(() => {
const axios = require("axios");
const url = "http://localhost:8080/demo/api/countries";
axios
.get(url)
.then((res) => {
setCountries(res.data);
})
.catch((error) => {
console.error("Error", error);
});
}, []); // Empty array of dependency makes it equivalent to componentDidMount
return (<div/>) // render your element like you'd do in a class component
};

Resources