how to show loader in react . using hooks - javascript

I am using axios for communicate with server.I want to show loader when user request to server and hide the loader when request is complete
So i make a custom component to do this task .but my UI hang when I click multiple times on same button
const Loader = () => {
const { loadingCount } = useLoadingState(),
{showLoading, hideLoading} = useLoadingActions();
useEffect(()=>{
const self = this
axios.interceptors.request.use(function (config) {
showLoading();
return config
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
// spinning hide
// self.props.loading(false)
hideLoading()
return response;
}, function (error) {
hideLoading();
return Promise.reject(error);
});
})
return (
<div>
{loadingCount > 0 ?<div style={{position:"fixed",display:"flex",justifyContent:"center",alignItems:"center",width:'100%',height:'100%',zIndex:999999}}>
{/*{loadingCount > 0 ? */}
<Spin tip="Loading..." style={{zIndex:999999}}></Spin>
{/*: null}*/}
</div>: null}
</div>
);
};
Problem is on useeffect
when I comment out useEffect code it works perfectly .
NoTe : showloading and hideloading increase and decrease the loading count.
I think I have deallocate axios object the when component is unmount.???

Add empty array to sencod parameter to useEffect.
It works like componentDidMount() in functional component.
const { useState, useEffect } = React;
const Counter = () => {
const [count, setCount] = useState(0)
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsLoaded(true);
}, 3000);
}, []); // here
return (
<div>
{
isLoaded &&
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
}
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="app"></div>

i usualy use this code to show loading when request data is processing and hide when it's done
const Loader = () => {
const {data, setdata} = useState([])
useEffect(()=>{
axios.get('your host').then(res => {
setdata(res.data);
}).catch(err => {
setdata(res.data);
}
});
return (
<div>
{data.length > 0
?
<div style={{position:"fixed",display:"flex",justifyContent:"center",alignItems:"center",width:'100%',height:'100%',zIndex:999999}}> </div>
:
<Spin tip="Loading..." style= {{zIndex:999999}}>
</Spin>
</div>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

First I created Loading components in shared folder. I am using Daisy UI, that's why you have to first install tailwind & daisy otherwise it will not work. My Loading Code:
import React from 'react';
const Loading = () => {
return (
<div className="flex items-center justify-center ">
<div className="w-16 h-16 border-b-2 border-gray-900 rounded-full animate-spin"></div>
</div>
);
};
export default Loading;
Then I am using this Loading component in my Allproduct component. For viewing Loading i created Reload useState.You will see below in my code, that will help my loader show when fetching time is very long.
import React, { useEffect, useState } from 'react';
import Loading from '../Shared/Loading';
import AllProduct from './AllProduct';
const AllProducts = () => {
const [products, setProduct]=useState([])
const [Reload, setReload] = useState(true);
useEffect(()=>{
fetch('https://stormy-hamlet-97462.herokuapp.com/products/')
.then(res=>res.json())
.then(data=>{setProduct(data)
setReload(false)})
},[])
if(Reload){
return <Loading></Loading>
}
return (
<div>
<h4 className='text-4xl text-primary text-center sm:w-full px-32 mx-5
lg:my-12 '>All Products</h4>
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5'>
{
products.map(product=><AllProduct key={product._id} product={product} ></AllProduct>)
}
</div>
</div>
);
};
export default AllProducts;

Related

Error: Objects are not valid as a React child (found: [object Promise]).….. While getting data from supabase

I am having a problem while getting data from supabase .
Could any one help me
`
import Link from "next/link";
import { supabase } from "../../supabase"
async function Index(){
const { data, error} = await supabase.from("Employees").select("*")
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
`
I tried using map, other function, and looked it up yet nothing works
The problem is how you are fetching the data. Try fetching your data inside an useEffect hook:
import Link from "next/link";
import { supabase } from "../../supabase";
import { useState, useEffect } from "react";
function Index() {
// const { data, error } = await supabase.from("Employees").select("*")
const [data, setData] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
let cancelFetch = false; // to avoid race conditions on React18
supabase
.from("Employees")
.select("*")
.then((res) => {
if (!cancelFetch) {
setData(res.data);
setError(res.error);
}
});
return () => {
cancelFetch = true;
};
}, []);
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return <h1>{index.name}</h1>;
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
);
}
export default Index;
More info on fetching and useEffect here: https://beta.reactjs.org/apis/react/useEffect#fetching-data-with-effects
Your source code is invalid. React components should always be a function (or class) that returns a react object. it does not accept a promise that returns a react object.
You will probably want to use react's useEffect to solve this problem:
import { useState, useEffect } from "react";
import Link from "next/link";
import { supabase } from "../../supabase"
async function Index(){
const [data, setData] = useState()
const [error, setError] = useState()
useEffect(() => {
supabase.from("Employees").select("*")
.then(data => setData(data))
.catch(err => setError(err))
}, [])
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
Your component cannot be async, because it returns a Promise and React doesn't like that.
There is a cool function on Next.js that allows you to fetch data asynchronously, try that:
function Index({ data }) {
return (
<>
<div className="container flex justify-center">
<h1>Employees</h1>
</div>
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
<Link href="/employees/addemployee">
<h1>Add employee</h1>
</Link>
</>
)
}
export default Index;
export async function getServerSideProps() {
const { data, error} = await supabase.from("Employees").select("*")
return {
props: {
data: data
}
}
}
More here: https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
Based on the way you are fetching data, I believe you are using next13 and you are in app directory. When you rendered jsx
{data.map((index) => {
return (
<h1>{index.name}</h1>
)
})}
index refers to each element inside the data array. Most likely index.name is an object. that is why it is throwing that error.
console.log("index name",index.name)
If you are using async functional component, you should be using Suspense api. Create a separeate component maybe async Users, fetch the data inside this component, and when you want to display the users inside the Index
import {Suspense} from "react"
function Index(){
return (
<>
....
<Suspense fallback={ <h1>Users are loading...</h1>} >
<Users/>
</Suspense>
....
</>
)
}
You only use async component inside app folder and server component.

Add item from Fetch API to Array and Displaying new array in React

I'm learning react for the first time, I have an app where it fetches some data from a public API. I currently have it show 10 cards with random items from the API, and I have added a button to fetch a random item from the API and add it to the array, I managed to get the new item added to the array using push() but it does not show in the app itself. How can I make it that the new item is shown in the app as well?
Here is my code
Home.js
import { useState, useEffect} from "react";
import Card from './Card';
const Home = () => {
const [animals, setAnimals] = useState([]);
const handleDelete = (id) => {
const newAnimals = animals.filter(animal => animal.id !== id);
setAnimals(newAnimals);
}
useEffect(() => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/10')
.then(res => {return res.json()})
.then(data => {
setAnimals(data);
});
}, []);
const handleAddAnimal = () => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/')
.then(res => {return res.json()})
.then(data => {
animals.push(data);
console.log(animals);
//what to do after this
})
}
return (
<div className="home">
<h2>Animals</h2>
<button onClick={handleAddAnimal}>Add Animal</button>
<Card animals={animals} handleDelete={handleDelete}/>
</div>
);
}
export default Home;
Card.js
const Card = ({animals, handleDelete}) => {
// const animals = props.animals;
return (
<div className="col-3">
{animals.map((animal) => (
<div className="card" key={animal.id}>
<img
src={animal.image_link}
alt={animal.latin_name}
className="card-img-top"
/>
<div className="card-body">
<h3 className="card-title">{animal.name}</h3>
<p>Habitat: {animal.habitat}</p>
<button onClick={() => handleDelete(animal.id)}>Delete Animal</button>
</div>
</div>
))}
</div>
);
}
export default Card;
App.js
import Navbar from './navbar';
import Home from './Home';
function App() {
return (
<section id="app">
<div className="container">
<Navbar />
<div className="row">
<Home />
</div>
</div>
</section>
);
}
export default App;
Screenshot of what I see now
screenshot
(I was also wondering how to fix the items going down instead of side by side but wanted to fix the add button first)
Let me know if there's anything else I should add, any help is appreciated, thank you!
Rather using array.push() method. You try using
setTheArray([...theArray, newElement]); e.g in your case it will be setAnimals([...animals,data]) in your onClick event.
Let me know doest it solve your issue or not.
const handleAddAnimal = () => {
fetch('https://zoo-animal-api.herokuapp.com/animals/rand/')
.then(res => {return res.json()})
.then(data => {
setAnimals([...animals,data])
console.log(animals);
//what to do after this
})
}

How to create "More" button on React?

I have React components :
Main.jsx
import { useState, useEffect } from "react";
import { Preloader } from "../Preloader";
import { Pokemons } from "../Pokemons";
import { LoadMore } from "../LoadMore";
function Main() {
const [pokemons, setPokemons] = useState([]);
const [loading, setLoading] = useState(true);
const [pokemonsPerPage] = useState("20");
const [pokemonOffset] = useState("0");
useEffect(function getPokemons() {
fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${pokemonsPerPage}&offset=${pokemonOffset}`
)
.then((responce) => responce.json())
.then((data) => {
data.results && setPokemons(data.results);
setLoading(false);
});
}, []);
return (
<main className="container content">
{loading ? <Preloader /> : <Pokemons pokemons={pokemons} />}
<LoadMore />
</main>
);
}
export { Main };
LoadMore.jsx
import React from 'react'
function LoadMore() {
return (
<div className="button_container">
<a class="waves-effect waves-light btn-large" id="more">
More...
</a>
</div>
);
}
export { LoadMore };
I have created a button in the component. After clicking on it, the next 20 elements should be loaded. I created const [pokemonsPerPage] = useState("20"); and const [pokemonOffset] = useState("0"); in order to substitute these values into the request. The first is responsible for the number of objects on the page, the second is responsible for which element to start counting from. That is, now 20 elements are being output, starting from the very first one. (If you change const [pokemonOffset] = useState("0"); to 20, then the output will start from 21 elements). I'm sure that this is necessary for implementation, but I don't know what to do next
Help me complete this functionality
Here is what your Main.jsx should look like.
import { useState, useEffect } from "react";
import { Preloader } from "../Preloader";
import { Pokemons } from "../Pokemons";
import { LoadMore } from "../LoadMore";
function Main() {
const [pokemons, setPokemons] = useState([]);
const [loading, setLoading] = useState(true);
const [pokemonsPerPage] = useState(20);
const [page,setPage] = useState(1);
function getPokemons(pokemonOffset) {
fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${pokemonsPerPage}&offset=${pokemonOffset}`
)
.then((responce) => responce.json())
.then((data) => {
data.results && setPokemons(data.results);
setLoading(false);
});
}
useEffect(() => {
const offset= page*pokemonsPerPage -pokemonsPerPage;
getPokemons(offset);
}, [page]);
return (
<main className="container content">
{loading ? <Preloader /> : <Pokemons pokemons={pokemons} />}
<LoadMore next={()=>setPage(p=>p+1)} />
</main>
);
}
export { Main };
while your Loadmore.jsx
import React from 'react'
function LoadMore(next) {
return (
<div className="button_container">
<a type="button" onclick={next} class="waves-effect waves-light btn-large" id="more">
More...
</a>
</div>
);
}
export { LoadMore };

I am getting state undefined error when reloading page

Everything is fine the first time but get error and undefined state the time I refresh the page
and also if I comment the h1 and p tags and uncomment then the error resolves and hits again I refresh
Here is my code
import { useState, useEffect } from "react";
function App() {
var [state, setState] = useState({});
useEffect(() => {
fetch(
"https://api.openweathermap.org/data/2.5/find?q=kolkata&units=metric&appid=907c99e1a96b5d38487d8d9c19b413fc"
)
.then((doc) => {
return doc.json();
})
.then((doc) => {
setState(doc);
});
}, []);
return (
<div className="App">
<div className="welcome">
<h1>
{state.list[0].main.temp}
<sup> o</sup>C
</h1>
<p>Feels Like {state.list[0].main.feels_like}</p>
<img src="https://openweathermap.org/img/w/50d.png" alt="haze" />
</div>
</div>
);
}
export default App;
Your state is an empty object Initially ,you have to put a check ,like I did below.
import { useState, useEffect } from "react";
function App() {
var [state, setState] = useState({});
useEffect(() => {
fetch(
"https://api.openweathermap.org/data/2.5/find?q=kolkata&units=metric&appid=907c99e1a96b5d38487d8d9c19b413fc"
)
.then((doc) => {
return doc.json();
})
.then((doc) => {
setState(doc);
});
}, []);
return (
<div className="App">
<div className="welcome">
{Object.keys(state).length>0?<><h1>
{state.list[0].main.temp}
<sup> o</sup>C
</h1>
<p>Feels Like {state.list[0].main.feels_like}</p></>:""}
<img src="https://openweathermap.org/img/w/50d.png" alt="haze" />
</div>
</div>
);
}
export default App;
or If you want to avoid Object.keys make default state null
import { useState, useEffect } from "react";
function App() {
var [state, setState] = useState(null);
useEffect(() => {
fetch(
"https://api.openweathermap.org/data/2.5/find?q=kolkata&units=metric&appid=907c99e1a96b5d38487d8d9c19b413fc"
)
.then((doc) => {
return doc.json();
})
.then((doc) => {
setState(doc);
});
}, []);
return (
<div className="App">
<div className="welcome">
{state?<><h1>
{state.list[0].main.temp}
<sup> o</sup>C
</h1>
<p>Feels Like {state.list[0].main.feels_like}</p></>:""}
<img src="https://openweathermap.org/img/w/50d.png" alt="haze" />
</div>
</div>
);
}
export default App;
UseEffect is called when the component is completely rendered for the first time, so when you use state.list[0].main.attribute, you're calling an undefined, because your initial state is {}.
Try this:
<div className="welcome">
<h1>
{state.list[0] ? state.list[0].main.temp : ""}
<sup> o</sup>C
</h1>
<p>Feels Like {state.list[0] ? state.list[0].main.feels_like : ""}</p>
<img src="https://openweathermap.org/img/w/50d.png" alt="haze" />
</div>

React - share state beetween separated React Apps possible?

I wrote a React-App and now I want to implement some Tab-Navigation.
The problem is, my react app is integrated in a Symfony-Project that uses Twig templates.
So my React-App lives in templates/dashboard/index.html.twig:
{% block content %}
<div id="react-app"></div>
{% endblock %}
And the tab-menu lives in templates/navigation/navbar.html.twig :
<div class="flex-1 px-4 flex justify-between">
<div id="react-tabs"></div>
</div>
react-App.js renders both of the apps.
import React from 'react';
import {render} from 'react-dom';
import {ready} from '../../helper/vanilla-helper'
import ReactTabs from "./ReactTabs";
function ReactApp() {
return (
<div className="App p-20 bg-blue-300">
Blubber
</div>
);
}
export default ReactApp;
ready(() => {
render(
<ReactApp/>,
document.getElementById('react-app')
);
render(
<ReactTabs/>,
document.getElementById('react-tabs')
);
});
I did quite a lot of research in the internet about sharing state.
But it seems all of this is only related to sharing state inside of ONE ReactJs App and between their components.
In my case I need to share state between two apps.
Is this even possible or should I take a complete different approach to this?
You can make a simple store (as in state management) to share the state between components. If you need a more serious solution, I suggest you should look into Redux / MobX or some other state management tool.
In the snippet only a very basic counter is shared - it's easier to follow through like this:
const { useState, useEffect, useMemo } = React
// observer pattern
const makeObservable = (target) => {
let listeners = []
let value = target
function get() {
return value
}
function set(newValue) {
if (value === newValue) return
value = newValue
listeners.forEach((l) => l(value))
}
function subscribe(listenerFunc) {
listeners.push(listenerFunc)
return () => unsubscribe(listenerFunc) // can be used in the useEffect
}
function unsubscribe(listenerFunc) {
listeners = listeners.filter((l) => l !== listenerFunc)
}
return {
get,
set,
subscribe,
}
}
const simpleStore = makeObservable({ count: 0 })
const useStore = () => {
const [counter, setCounter] = useState(simpleStore.get())
useEffect(() => {
return simpleStore.subscribe(setCounter)
}, [])
const actions = useMemo(() => {
return {
incrementCount: () => simpleStore.set({ ...counter, count: counter.count + 1 }),
decrementCount: () => simpleStore.set({ ...counter, count: counter.count - 1 }),
}
}, [counter])
return {
state: counter,
actions
}
}
const App = () => {
const { state, actions } = useStore()
return (
<div>
{state.count}
<button
onClick={() => actions.incrementCount()}
>
INCREMENT
</button>
<button
onClick={() => actions.decrementCount()}
>
DECREMENT
</button>
</div>
)
}
const Tabs = () => {
const { state, actions } = useStore()
return (
<div>
{state.count}
<button
onClick={() => actions.incrementCount()}
>
INCREMENT
</button>
<button
onClick={() => actions.decrementCount()}
>
DECREMENT
</button>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
ReactDOM.render(
<Tabs />,
document.getElementById('tabs')
);
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<div id="app"></div>
<div id="tabs"></div>
Thanks streletss for the great example on the makeObservable & the custom hook!

Categories

Resources