Whenever I get data on my page, after a few seconds, my whole react app disappears as in the root div in the html is left completely empty like this <div id="root"></div> as if there is nothing. This is happening on all my other projects too even when I create a new one, this disappearing of the react keeps happening sometimes even without adding any logic, it refuses to render plain html. The errors I get for now on this current project on the console is this
characters.map is not a function
I know not what could be causing this but my code looks like this for now starting with the App.js file. I am extracting data from an api.
import {BrowserRouter, Route, Routes} from "react-router-dom"
import Home from "./components/Home";
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
And then followed by the CharactersListing page which is supposed to render all the characters of the show
import React, {useEffect, useState} from 'react'
import CharacterCard from './CharacterCard'
export default function BadListings() {
const [characters, setCharacters] = useState([])
useEffect(() => {
const getData = async () => {
await fetch("https://www.breakingbadapi.com/api/characters")
.then(response => {
setCharacters(response.json());
console.log(characters);
})
.catch(err => console.log("There must have been an error somewhere in your code", err.message));
}
getData();
});
return (
<div className='container'>
{characters.map(character => (
<div>
<CharacterCard name={character.name} status={character.status} image={character.img} />
</div>
))}
</div>
)
}
And finally, the CharacterCard.js
import React from 'react'
import "../styles/styles.css"
export default function CharacterCard({name, status, image}) {
return (
<div className='card'>
<h1>{name}</h1>
<h2>{status}</h2>
<img src={image} alt="umfanekiso" className='imgur' />
</div>
)
}
I do not know what could be causing this. I have never had this issue it just started today. What could be causing it
Issues
The issue is that you are not setting the characters state to what you think it is. response.json() returns a Promise object and doesn't have a map property that is a function to be called.
The useEffect hook is also missing a dependency, so anything that triggers this BadListings component to rerender will also retrigger this useEffect hook, which updates state and triggers another rerender. The code is likely render looping.
Solution
The code should wait for the response.json() Promise to resolve and pass that result value into the characters state updater function. Note that I've also rewritten the logic to use async/await with try/catch as it is generally considered anti-pattern to mix async/await with Promise chains.
Add a dependency array to the useEffect hook. Since I don't see any dependencies use an empty array so the effect runs only once when the component mounts.
Promise chain Example:
useEffect(() => {
fetch("https://www.breakingbadapi.com/api/characters")
.then(response => response.json()) // <-- wait for Promise to resolve
.then(characters => setCharacters(characters)
.catch(err => {
console.log("There must have been an error somewhere in your code", err.message)
});
}, []); // <-- add empty dependency array
async/await Example:
useEffect(() => {
const getData = async () => {
try {
const response = await fetch("https://www.breakingbadapi.com/api/characters");
const characters = await response.json(); // <-- wait for Promise to resolve
setCharacters(characters);
} catch(err) {
console.log("There must have been an error somewhere in your code", err?.message);
};
}
getData();
}, []); // <-- add empty dependency array
Don't forget to add a React key to the mapped characters:
{characters.map((character) => (
<div key={character.char_id}> // <-- Add React key to outer element
<CharacterCard
name={character.name}
status={character.status}
image={character.img}
/>
</div>
))}
characters is a string and strings don't have .map() method, that's why React is crashing. And since React's crashed, it couldn't mount generated HTML to the #root.
You can use [...strings] to use .map() method.
You are exporting CharacterCards and not BadCards.
Please change all BadCards in your CharactersListing page to CharacterCards
There is an explaination What does "export default" do in JSX?
Great instinct to look for errors in the console.
Umut Gerçek's answer is correct, but I'd add an additional suggestion: if you're going to map over something, you should instantiate it in state as a thing that can be mapped. Set its initial state to an array:
const [characters, setCharacters] = useState([])
Note the capital 'C' in the setter; that is the useState convention.
Then set characters as you're currently doing:
setCharacters(response.json());
And your map should work regardless of the result of your fetch, and handle multiple items, too.
Related
This question already has answers here:
Why useEffect running twice and how to handle it well in React?
(2 answers)
Closed last month.
I'm trying to get data from firebase in react, using useEffect to avoid creating loops.
My code works so far, but I get the results twice. When I tried to find out what the problem was, I found that the data was also retrieved twice. Because the whole code section is executed twice.
--> i get the "Did request!" from console.log("Did request!") 2x times
import React, { useEffect, useState } from "react";
import { db } from "../firebase-config";
import { collection, getDocs } from "firebase/firestore";
function MusicList() {
const [musicList, setMusicList] = useState([]);
const getData = async () => {
try {
const querySnapshot = await getDocs(collection(db, "music"));
querySnapshot.forEach((doc) => {
setMusicList((oldArray) => [...oldArray, doc.data()]);
});
console.log("Did request!");
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getData();
}, []);
return (
<div className="MusicList">
{musicList.map((music) => {
return <div key={music.id}>{music.songName}</div>;
})}
</div>
);
}
export default MusicList;
Being relatively new to React and the concept of "useEffect" I don't know exactly why this is happening.
This is most likely because you have React strict mode enabled? It does this very annoying thing where it renders components twice. Remove it and it should only render once. Let me know if it works:
<StrictMode> <<--- remove this
<App />
</StrictMode> <<--- remove this
UPDATE: I should mention this is a quick fix. The double rendering is done to ensure you've put in features such as query caching etc where bugs can be detected by double rendering.
Further information: https://github.com/facebook/react/issues/24502#issuecomment-1118754581
Im new in react.
I'm Created two file App.js and UseEffect.js
I'm Learn about lifecycle in react with function.
So When I See in console, that's render multiple time.
You can see my picture below.
My Console In Browser
This Is My Code
UseEffect.js
import React, {useState, useEffect} from "react";
function MyFunction(){
console.log('-> Function Init')
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(prevState => {
return prevState+1
})
}
//LifeCycle
useEffect(() => {
console.log('my first effect')
})
console.log(`-> Start Render (${count})`)
return(
<div>
<h1>Function Component</h1>
<p>
<button onClick={handleCount}>Count</button>
{count}
</p>
</div>
)}
export default MyFunction
App.Js
import './App.css';
import UseEffect from './components/UseEffect'
function App() {
return (
<div className="App">
<UseEffect />
</div>
);
}
export default App;
How do it's work?, I Want it. it's just render one times.
Your useEffect call is missing a dependency array. When you want it to run only at the initial render, you need to pass it an empty array as its dependencies.
useEffect(() => {
console.log('my first effect')
}, [])
For further details, see this question.
Why it renders twice:
It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.
-gaearon
TLDR: It's a feature not a bug.
I have a rails (7.0.2) application and just installed React. I'm very new to react and can't seem to understand why it looks like my component is loading multiple times, the first time with an empty value for props and the second time with the correct values for props.
App.js:
import "./App.css";
import axios from "axios";
import Customers from "./components/customers";
import { useEffect, useState } from "react";
const API_URL = "http://localhost:3000/internal_api/v1/customers";
function getAPIData() {
return axios.get(API_URL).then((response) => response.data);
}
function App() {
const [customers, setCustomers] = useState([]);
useEffect(() => {
let mounted = true;
getAPIData().then((items) => {
if (mounted) {
setCustomers(items);
}
});
return () => (mounted = false);
}, []);
console.log('LOADED App.js');
return (
<div className="App">
<h1>Hello</h1>
<Customers customers={customers} />
</div>
);
}
export default App;
and customers.js:
import React from "react";
function Customers(props) {
console.log('LOADED customers.js');
return (
<div>
<h1>These customers are from the API</h1>
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
})}
</div>
);
}
export default Customers;
When I remove this part of the code and reload the page, my props come through correctly when looking in console. Then, when I put the code back and save (without reloading), it displays correctly.
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
However, as soon as I reload again, I get the same following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
It seems as though the first time everything renders, props is empty. Then the second time, it is full with the data. I checked my rails app and it only hits the API once. What am I doing wrong?
More log outputs:
React component rendering multiple times?
React will render fast before completing the request in use Effect
so in first render customers array will be empty
when request is fulfilled, you are changing state, So react will re-render the component
Only component that uses state reloads when the state is changed this is required else UI will not update
failing when reloading the page? | Failed on Initial Load
Since in Initial render customers will have no data customers.data will be undefined so it will not have map
to bypass this error use props.customers?.data && props.customers.data?.map() addding question mark means expression will be evaluated if not undefined
Source - Optional_chaining
In my code example, my useEffect is subscribed to postId, which is not changing at any point. Yet useEffect is still being run.
Is there a way to prevent this from running and only launch if I change it?
import React, {useState, useEffect} from 'react';
import Vim from './Vim';
import './Main.css';
function Main():JSX.Element {
const [postId,updatePostId] = useState<number|null>(null)
const [content, updateContent] = useState<string>('default text');
const apiUrl = 'http://127.0.0.1:8000/'
useEffect(()=>{
// Detect change in PostID & go download it.
// Ignore if we are coming from null->number
console.log('Why am I running?')
fetch(apiUrl+'get_post/'+postId)
.then(res=>res.json())
.then(result=>console.log(result))
},[postId])
function loadPost(pid:number):string|null{
// fetch from API, load post content
console.log('I can access:'+postId)
return null;
}
function backLinks():JSX.Element{
return(
<div className="backlinks">
</div>
)
}
return (
<div className='main'>
<Vim content={content} />
</div>
)
}
export default Main
In fact in the first run, postId is null since you initialize it in that way. So you need to take care of that as well.
useEffect either accepts an array of dependencies or you could leave it empty. In the later case it just run once since there is no dependency, however in the first case it both listen for dependencies changing and also runs when the component mounts for the first time.
useEffect(()=>{
if (postId == null) return;
...
}, [postId]);
For a more general approach you can have something like below.
const [ready, setReady] = useState(false);
useEffect(() => {
setReady(true);
}, [])
useEffect(()=>{
if (!ready) return;
...
}, [postId, ready]);
The above solution is fair enough in most situations but I suggest you to handle it by creating a ref, assigning it to your DOM element and wait until that ref become available. So you are sure that your component is being rendered for the first time.
const isMounted = useRef(null);
useEffect(()=>{
if (!isMounted.current) return;
// do your fetch stuff here
fetch(apiUrl+'get_post/'+postId)
.then(res=>res.json())
.then(result=>console.log(result))
}, [isMounted]);
<div ref={isMounted}></div>
This way you don't need an extra re-render since updating refs does not lead to a re-render.
I'm using Preact for the first time.
I simply created a new project with preact-cli and this default template: https://github.com/preactjs-templates/default.
In app.js I'm trying to use this code:
import { Router } from 'preact-router';
import Header from './header';
import Home from '../routes/home';
import Profile from '../routes/profile';
// I added this function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const App = async () => { // I added "async" and the "{" in this line
await sleep(3000) // I added this line
return ( // I added this line
<div id="app">
<Header />
<Router>
<Home path="/" />
<Profile path="/profile/" user="me" />
<Profile path="/profile/:user" />
</Router>
</div>
)
} // I added this line
export default App;
But unfortunately browser's gives me error:
Uncaught Error: Objects are not valid as a child. Encountered an object with the keys {}.
Why?
It works if I do not use async/await.
Disclaimer: I work on Preact.
Our debug addon (preact/debug) will print this error whenever an invalid object is passed as a child that doesn't match the expected return type of h/createElement, usually called vnode:
const invalidVNode = { foo: 123 };
<div>{invalidVNode}</div>
In your case your component function returns a Promise which is an object in JavaScript. When Preact renders that component the render function will NOT return a vnode, but a Promise instead. That's why the error occurs.
Which poses the question:
How to do async initialization?
Once triggered, the render process in Preact is always synchronous. A component that returns a Promise breaks that contract. The reason it is that way is because you usually want to show at least something, like a spinner, to the user, while the asynchronous initialization is happening. A real world scenario for that would be fetching data via the network for example.
import { useEffect } from "preact/hooks";
const App = () => {
// useEffect Hook is perfect for any sort of initialization code.
// The second parameter is for checking when the effect should re-run.
// We only want to initialize once when the component is created so we
// pass an empty array so that nothing will be dirty checked.
useEffect(() => {
doSometThingAsyncHere()
}, []);
return (
<div id="app">
<Header />
<Router>
<Home path="/" />
<Profile path="/profile/" user="me" />
<Profile path="/profile/:user" />
</Router>
</div>
)
}
Reactjs is a component library. At the core it has a function like
React.createElement(component, props, ...children)
Here the first parameter is the component that you want to render.
When you are putting await sleep(3000) the function is not returning any valid children/html object rather it is returning an empty object.
that's why you are getting this error.