React Hooks - useEffect still being called even when object is empty - javascript

I have a question on React Hooks. This is a sample of my code :-
import React, { useState, useEffect } from "react";
import Card from "./Card";
const CardsBoard = () => {
useEffect(() => {
doRatingClickProcessing()
}, [ratingObj])
const doRatingClickProcessing = () => {
const { player, title, rating } = ratingObj
}
return (
<div className="container-fluid justify-content-center">
<div className="row">
<div className="col-md-6">
<Card
cardInfo={player1Card}
player={1}
showCard={visiblePl1}
clickableRatings = {clickableRatings}
onClick={ratingObj => setRatingObj(ratingObj)}
/>
</div>
<div className="col-md-6">
<Card
cardInfo={player2Card}
player={2}
showCard={visiblePl2}
clickableRatings = {clickableRatings}
onClick={ratingObj => setRatingObj(ratingObj)}
/>
</div>
</div>
)}
</div>
)
}
export default CardsBoard
Then in the card component I am returning the ratingObj successfully when the user clicks on a rating.
In the Card Component I have something like this:-
<div
className="col-md-2 text-left card-rating-color"
onClick={() =>
onClick({
player: player,
title: row[0].title,
rating: row[0].rating,
})
}
>
{row[0].rating}
</div>
However I am puzzled why useEffect() is triggered even when the Card component is loaded, and ratingObj is still empty. Shouldn't it be triggered only if the ratingObj is filled up?
Thanks for your help and time

useEffect will call at least once. it doesn't matter either your object is updating or not because when you write
useEffect(()=>{
},[ratingObj]);
In above code you are passing object into square brackets right. That means you are mentioning dependencies as e second parameter and empty [] in argument list will call once at least. After that, it depends on ratingObj that you have passed in.

import React, {useState,useMemo} from 'react';
const App = () => {
const [name, setName] = useState('');
const [modifiedName, setModifiedName] = useState('');
const handleOnChange = (event) => {
setName(event.target.value);
}
const handleSubmit = () => {
setModifiedName(name);
}
const titleName = useMemo(()=>{
console.log('hola');
return `${modifiedName} is a Software Engineer`;
},[modifiedName]);
return (
<div>
<input type="text" value={name} onChange={handleOnChange} />
<button type="button" onClick={handleSubmit}>Submit</button>
<Title name={titleName} />
</div>
);
};
export default App;
const Title = ({name}) => {
return <h1>{name}</h1>
}

Related

My React App state is not in sync with my Firebase

I am having a hard time getting my React App working properly.
The thing is that I tried to use UseEffect hooks only to run side effects in my app and this has brought me some problems.
In this simple component I have a chat that get data from Firebase and is capable of updating the Db. I have no problem with the Firebase side but on the front end, the first render is not able to get me the messages into state properly.
I feel that it has of course something to do with async behaviors.
I will try to explain you the flow of my component :
The message text is kept in a const in state call "inputText"; when the form is submited a const call "numberOfMessageSent" is incremented; I have a UseEffect Hook that has [numberOfMessageSent] in its depedency; so after the first mount of the component and when "NumberOfMessageSent" increments the callback will fire; this callback fires 2 async functions: one to fetch the current discussion from the db and another to create a discussion object or update an existing one into the Db. I have a condition :
"numberOfMessagesSent !== 0 && asyncWarperCreateDiscussionInDb()" in the UseEffect Hook so a new discussion empty discussion won't be created the first this component mount.
My problem is that no discussion is displayed (nor properly fetched and stored into state) BEFORE I send a first message. After I send this first message everything works properly.
Can someone help me to understand this better ?
Thank you very much
here is my code :
import React, { useContext, useEffect, useState } from "react";
import "./card-medium-message.style.scss";
import likeEmpty from "./like-empty.png";
import likeFull from "./like-full.png";
import cancel from "./cancel.png";
import send from "./send.png";
import back from "./back.png";
import { useNavigate, useParams } from "react-router-dom";
import { UsersListContext } from "../../context/usersList-context/users-list-context";
import { UserContext } from "../../context/user-context/user-context";
import {
createDiscussionInDb,
goFetchDiscussionInDb,
goFetchDisscussion,
} from "../../utils/firebase";
const CardMediumMessage = () => {
const params = useParams();
const { usersListCTX } = useContext(UsersListContext);
const { currentUserContext } = useContext(UserContext);
const currentUserClickedOn = usersListCTX.filter(
(user) => user.displayName === params.name
);
console.log(currentUserContext);
console.log(currentUserClickedOn[0]);
const [messages, setMessages] = useState([]);
const [inputText, setInputText] = useState("");
const [numberOfMessagesSent, setNumberOfMessagesSent] = useState(0);
const asyncWarperFetchDiscussionInDb = async () => {
if (currentUserClickedOn[0]) {
const discussion = await goFetchDiscussionInDb(
currentUserContext.displayName,
currentUserClickedOn[0].displayName
);
setMessages(discussion.messages);
}
};
const asyncWarperCreateDiscussionInDb = async () => {
await createDiscussionInDb(
currentUserContext.displayName,
currentUserClickedOn[0].displayName,
inputText
);
resetField();
};
useEffect(() => {
numberOfMessagesSent !== 0 && asyncWarperCreateDiscussionInDb();
asyncWarperFetchDiscussionInDb();
console.log(
"this is written after first render of the component or numberOfMessagesSent was updated"
);
}, [numberOfMessagesSent]);
const messageSubmit = async (e) => {
e.preventDefault();
if (inputText == "") {
return;
}
setNumberOfMessagesSent(numberOfMessagesSent + 1);
};
const textChanged = (e) => {
setInputText(e.target.value);
};
const resetField = () => {
setInputText("");
};
const navigate = useNavigate();
messages && console.log(messages);
return (
<div className="card-medium-warp">
<div className="card-medium-message">
<div className="section1" onClick={() => navigate(-1)}>
<div className="profile-image-outer-circle">
{currentUserClickedOn[0] ? (
<img
src={`https://api.dicebear.com/5.x/micah/svg?seed=${currentUserClickedOn[0].displayName}`}
alt="avatar"
className="profile-image"
/>
) : undefined}
</div>
{currentUserClickedOn[0] ? (
<h2 className="name">{currentUserClickedOn[0].displayName} </h2>
) : undefined}
<div
className="back"
style={{ backgroundImage: `url(${back})` }}
></div>
</div>
<div className="section2">
{messages
? messages.map((messageObject, index) => (
<p
key={index}
className={
messageObject.by === currentUserContext.displayName
? "sender-message"
: "receiver-message"
}
>
{messageObject.message}
</p>
))
: undefined}
</div>
<form className="section3" onSubmit={messageSubmit}>
<input
type="text"
className="input"
placeholder="your message"
onChange={textChanged}
value={inputText}
autoFocus
/>
<div
className="send-message"
style={{ backgroundImage: `url(${send})` }}
></div>
</form>
</div>
</div>
);
};
export default CardMediumMessage;
I think I found the solution so I would like to share it :
My mistake was that I was calling functions that were async in themselves but I didn't chain them in an async/await manner.
This is what I am talking about :
const asyncWarperSequence = async () => {
numberOfMessagesSent !== 0 && (await asyncWarperCreateDiscussionInDb());
await asyncWarperFetchDiscussionInDb();
};
useEffect(() => {
console.log("UseEffect Fired");
asyncWarperSequence();
}, [numberOfMessagesSent]);

Same code in terms of localStorage worked in React 17, but doesn't work in React 18 [duplicate]

I am creating a react app which is using local storage. I am saving and array of objects to local storage.
when I try to save to local storage the data is saving.
and then when I refresh the page the saved data is becoming empty object,
like this [].
if any one knows why its happening please help me
import React, {useEffect, useState} from 'react';
import Addcontact from './Addcontact';
import './App.css';
import Contactlist from './Contactlist';
import { Header } from './Header';
function App() {
const keyy ="contactlist"
const [contacts, setcontacts] = useState([])
const contactshandler = (contact)=> {
console.log(contact)
setcontacts([...contacts, contact])
}
useEffect(() => {
const getdata = JSON.parse(localStorage.getItem(keyy))
getdata && setcontacts(getdata)
}, [])
useEffect(() => {
localStorage.setItem(keyy, JSON.stringify(contacts));
}, [contacts])
return (
<div className="ui container">
<Header />
<Addcontact contacts={contacts} contactshandler={contactshandler} />
<Contactlist contacts={contacts} />
</div>
);
}
app component
import React, { useState } from 'react'
function Addcontact({contacts, setcontacts, contactshandler}) {
const [user, setuser] = useState({username:'', email:''})
const addvalue = (e) => {
e.preventDefault();
console.log(user)
contactshandler(user)
setuser({username:'', email:''})
}
return (
<div>
<div className='ui main'>
<h2> Add Contact</h2>
<form className='ui form' onSubmit={addvalue}>
<div className=''>
<label>name</label>
<input name="name" placeholder='name' value={user.username} onChange={(e) => setuser({...user, username : e.target.value })} />
</div>
<div className='feild'>
<label>email</label>
<input email='email' placeholder='email' value={user.email} onChange={(e) => setuser({...user, email: e.target.value})} />
</div>
<button>add</button>
</form>
</div>
</div>
)
}
export default Addcontact
export default App;
add component
this is the value showing when saving after refresh this value becomes empty object
enter image description here
console
enter image description here
You don't need useEffect to read the data. You can initially read it.
const [contacts, setcontacts] = useState(JSON.parse(localStorage.getItem(keyy)) ?? [])
and remove
useEffect(() => {
const getdata = JSON.parse(localStorage.getItem(keyy))
getdata && setcontacts(getdata)
}, [])

React how to make a call to an API and display an array in an array of objects PokeAPI

I need help with a project i am working on, i want to be able to display the moves of a pokemon i type in, i can console.log an array of objects that have smaller arrays inside that i want to grab and display, how can i do so? ive tried the cluster of a method getPokemonNames which is supposed to get the names of the moves of the pokemon but this is where i couldnt think anymore.
import React, {useState} from 'react';
import PokemonName from './PokemonName';
import axios from 'axios';
function App() {
const [pokemon, setPokemon] = useState([])
const [loading, setLoading] = useState(false)
const [moves, setMoves] = useState([])
const getPokemon = async (name) =>{
setLoading(true);
const response = await axios.get(`https://pokeapi.co/api/v2/pokemon/${name}`)
const data = response.data;
setPokemon(data);
setMoves(data.moves);
setLoading(false);
console.log(data)
getPokemonNames(data.moves)
// const pokemonMovesAmount = pokemon.moves.map
}
const getPokemonNames = (data) =>{
console.log(data);
data.move.name.map((moves, key)=>(
<div key={key}>
<span>{moves.moves.name}</span>
</div>
))
}
return (
<>
<PokemonName getPokemon={getPokemon}/>
<div>
{!loading && pokemon ? (<div>
</div>): null}
<img src={pokemon.sprites?.front_default}/>
<div className="container">
{getPokemonNames}
</div>
</div>
</>
);
}
export default App;
this is the pokemon name component
import React, {useState} from 'react'
const onSubmit =(e) =>{
e.preventDefault();
}
export default function PokemonName(props) {
const [search, setSearch] = useState('');
return (
<div>
<div>
<h1>{search}</h1>
<form onSubmit={onSubmit}>
<input onChange={e => setSearch(e.target.value)} type ="text" placeholder="Search for Pokemon"></input>
<button onClick={(e) => props.getPokemon(search)}>Search</button>
</form >
</div>
</div>
)
}
EDIT, this shows a bit more about the data i get back after searching up the pokemon mew
First step, making sure you pass the parameter data
const [moves, setMoves] = useState([])
// since you set setMoves to be data.moves second step you can iterate over data only
return (
<>
<PokemonName getPokemon={getPokemon}/>
<div>
{!loading && pokemon ? (<div>
</div>): null}
<img src={pokemon.sprites?.front_default}/>
<div className="container">
{getPokemonNames(moves)}
</div>
</div>
</>
);
Second step, tweak your getPokemon method
const getPokemonNames = (data) => {
return data.map(moveD, key)=>(
<div key={key}>
<span>{moveD.move.name}</span>
</div>
))
}
There is an mistake in your getPokemonNames . You are trying to do data.move.name.map which means you are mapping "move" from inside "data". You need to map "moves" array. Here is how you can do it.
const getPokemonNames = (data) =>{
data.moves.map(item, key)=>(
<div key={key}>
<span>{item.move.name}</span>
</div>
))
}

how to display people data in the body

Hi guys trying to make an mini application in react JS , in that application i want to display data in the my body part when i do search on my textbar , so anyone tell me how can i do it or what should i do to display data?
App.js This is my main component
import './App.css';
import Star from './Star';
import People from './People';
import Planet from './Planet';
function App(props) {
const {people,planet} = props;
return (
<div className="App">
<Star />
<People data={people}/>
<Planet data={planet}/>
</div>
);
}
export default App;
Star.js
This is my star component where i fetch my all star war apis
import React, { useState, useEffect } from 'react';
import './Star.css';
const Star = () => {
const [search, setSearch] = useState('');
const [people, setPeople] = useState([]);
const [planet, setPlanet] = useState([]);
const onSubmit = (e) => {
e.preventDefault();
if (search === "") {
alert("please Enter some value");
}
}
useEffect(() => {
async function fetchPeople() {
let result = await fetch("https://swapi.dev/api/people/?format=json");
let data = await result.json();
setPeople(data.results);
}
async function fetchPlanet() {
let result = await fetch("https://swapi.dev/api/planets/?format=json");
let data = await result.json();
setPlanet(data.results);
}
fetchPeople();
fetchPlanet();
}, [])
console.log("people", people);
console.log("planet", planet);
return (
<div>
<div className='container'>
<h2>Star War</h2>
<div className='jumbotron'>
<input type="text"
className="form-control"
placeholder='Search...'
value={search}
onChange={(e) => setSearch(e.target.value)} />
<span><button className='btn btn-secondary' onClick={onSubmit}>Search</button></span>
</div>
</div>
</div>
)
}
export default Star;
people.js
This is my people that i want to display in the my body part
import React from 'react';
const People = (props) => {
const { data } = props;
return (
<div className="row">
{data && data.map((people, i) => {
return (
<div className="col-md-3" key={i}>
<div className="card">
<div className="card-body">
<h4>{people.name}</h4>
</div>
</div>
</div>
)
})}
</div>
);
};
export default People;
If I understand correctly your issue, you want to display the People component and the issue is how to pass it the data fetched in the Star component.
The solution is to move the state of the data in the parent component so that it can be passed easily to all its children.
function App(props) {
const [people, setPeople] = useState([]);
const [planet, setPlanet] = useState([]);
useEffect(() => {
async function fetchPeople() {
let result = await fetch("https://swapi.dev/api/people/?format=json");
let data = await result.json();
setPeople(data.results);
}
async function fetchPlanet() {
let result = await fetch("https://swapi.dev/api/planets/?format=json");
let data = await result.json();
setPlanet(data.results);
}
fetchPeople();
fetchPlanet();
}, [])
return (
<div className="App">
<Star
people={people} //if you need these in the Star component
planet={planet} //if you need these in the Star component
/>
<People data={people}/>
<Planet data={planet}/>
</div>
);
}

How to pass down functions as a prop with the new React Hooks

I am practicing the new React hooks and I came with another question once again, cause I simply cannot find this on the internet.
I am trying to pass down a function as a prop from a function component to another function component. I am managing the states with the useState hook. In a class-based component you'd use this.props.addTodo(this.state) where I now have addTodo.addTodo(content). This feels kinda hacky. Isn't there a better way to do this? See the full code below.
Main app component
import React, { useState } from 'react';
import Todos from './Todos';
import AddTodo from './AddTodo';
function App() {
const [todos, setTodos ] = useState([
{id: 1, content: 'buy some milk'},
{id: 2, content: 'play mario kart'}
]);
const deleteTodo = (id) => {
const Todos = todos.filter(todo => {
return todo.id !== id
});
setTodos(Todos)
}
const addTodo = (todo) => {
console.log(todo) //Here I want to do something with the new todo value, which I got from the add todo component
}
return (
<div className="todo-app container">
<h1 className="center blue-text">Todo's</h1>
<Todos todos={todos} deleteTodo={deleteTodo} />
<AddTodo addTodo={addTodo} />
</div>
);
}
export default App;
The add todo's component
import React, { useState } from 'react';
function AddTodo(addTodo) {
const [content, setContent] = useState('');
const handleChange = (e) => {
setContent(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
addTodo.addTodo(content);
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>Add new todo</label>
<input type="text" onChange={handleChange}/>
</form>
</div>
)
}
export default AddTodo;
I am pretty new to actively sharing my code and asking help in this way. So please let me know if there is more info needed on this subject!
Best regards
destruct a prop obj
import React, { useState } from 'react';
function AddTodo({addTodo}) {
const [content, setContent] = useState('');
const handleChange = (e) => {
setContent(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
addTodo(content);
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>Add new todo</label>
<input type="text" onChange={handleChange}/>
</form>
</div>
)
}

Categories