useEffect is being called twice even if it state is changed once - reactjs

i made login hook called "useMakeQueryString". which is responsble for making queryString from Object.
import { useEffect, useState } from "react";
export default function useMakeQueryString(obj) {
const [queryString, setQueryString] = useState("");
const makeQueryString=(obj)=> {
let queryString1 = "";
for (let key in obj) {
//if (key instanceof Object) queryString1 += makeQueryString(obj);
queryString1 += key + "=" + obj[key] + "&";
}
setQueryString(queryString1.slice(0, queryString1.length - 1));
}
useEffect(()=>{
makeQueryString(obj)
},[obj])
return { queryString, makeQueryString };
}
then i imported that hook to my Google Component. on click of Component it calls the performAuth function and that function set the option state. and useEffect on option change is called. inside useEffect which is being called on option change i try to change queryString State. but the problem is useEffect on queryString change is being Called Twice
import useMakeQueryString from "../Login/LoginHook";
import { useEffect,useState } from "react";
export default function Google() {
const g_url = "https://accounts.google.com/o/oauth2/v2/auth";
const {queryString,makeQueryString} = useMakeQueryString({});
let [option,setOption] = useState({})
useEffect(() => {
console.log("length"+Object.keys(option).length)
if(Object.keys(option).length!=0) {
makeQueryString(option); // setQueryString(query);
}
}, [option])
useEffect(()=>{
if(queryString)
window.location = `${g_url}?${queryString}`;
},[queryString])
const performAuth = () => {
console.log("perform cliked")
const option1 = {
client_id: "432801522480-h02v02ivvti9emkd019fvreoisgj3umu.apps.googleusercontent.com",
redirect_uri: "http://localhost:3000/glogin",
response_type: "token",
scope: [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
].join(" "),
}
setOption(option1);
}
return (
<>
<button className="google-btn social-btn" onClick={() => performAuth()}>SignUp With Google</button>
</>
)
}

Related

change background image from parent with child data

im doing a weather app, and i need to change the background-image when the weather change.
in the parent i get my coords, and pass the coords to my weatherApp.jsx
(parent)
import { useEffect, useState } from 'react'
import './App.css'
import WeatherApp from './components/WeatherApp'
function App() {
const [coords, setCoords] = useState()
useEffect(() => {
const success = (pos) => {
const location = {
lat: pos.coords.latitude,
lon: pos.coords.longitude
}
setCoords(location)
}
navigator.geolocation.getCurrentPosition(success)
}, [])
console.log(coords);
return (
<div className="App">
<WeatherApp lon={coords?.lon} lat={coords?.lat}/>
</div>
)
}
export default App
in the child i use axios to get my data form openweathermap, but i need to change the background image from App.jsx
import axios from 'axios'
import React, { useEffect, useState } from 'react'
const WeatherApp = ({lon, lat}) => {
const [weather, setWeather] = useState()
const [temperature, setTemperature] = useState()
const [isCeslsius, setIsCeslsius] = useState(true)
useEffect(() => {
if (lat) {
const APIKey = "***"
const URL = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${APIKey}`
axios.get(URL)
.then(res => {
setWeather(res.data)
const temp = {
celsius: {
celsius_temp: `${Math.round(res.data.main.temp - 273.15)}°C`,
celsius_min: `${Math.round(res.data.main.temp_min - 273.15)}°C`,
celsius_max: `${Math.round(res.data.main.temp - 273.15)}°C`
},
farenheit: {
farenheit_temp: `${Math.round((res.data.main.temp - 273.15) * 9/5 + 32)}°F`,
farenheit_min: `${Math.round((res.data.main.temp_min - 273.15) * 9/5 + 32)}°F`,
farenheit_max: `${Math.round((res.data.main.temp_max - 273.15) * 9/5 + 32)}°F`
}
}
setTemperature(temp)
// trying to change the background image depending on the weather description react
if (weather.weather[0].description === "clear sky") {
let bgImg = {
backgrounImage: "url(../public/clear-sky.jpg)"
}
}
})
.catch(err => console.log(err))
}
}, [lat, lon])
console.log(weather);
const changeTemperature = () => setIsCeslsius(!isCeslsius)
maybe i could just use one component but i know there is a way to pass information from child to parent i just dont know how to
You need to lift the state up. You can do something like this.
App.jsx
const [bgImg, setBgImg] = useState('');
<div className="App" style={{backgroundImage: bgImg}}>
Pass setBgImage to child
<WeatherApp setBgImg={setBgImg} lon={coords?.lon} lat={coords?.lat}/>
In WeatherApp.jsx
let bgImg = "url(../public/clear-sky.jpg)";
props.setBgImg(bgImg);
This should set the background image of parent.
You can use useState hook and initialize it with the default value in parent component. Then pass the setter function to the WeatherApp component as p props.
In parent component
const [bgImgUrl, setBgImgUrl] = useState('your default URL here');
Pass the props to the WeatherApp component
<WeatherApp lon={coords?.lon} lat={coords?.lat} setBgImgUrl={setBgImgUrl} />
Then in the weather App component, you use to write your function
....
setTemperature(temp)
if (weather.weather[0].description === "clear sky") {
let bgImg = {
backgrounImage: "url(../public/clear-sky.jpg)"
}
// update the state
props.setBgImgUrl(bgImg)
}
....

React function on pageload

I want the heartsDisplay function call on pageload, but doing it like that causes an error. It works only with on click. How do I do this in React?
Or maybe there is a way to add default value for hearts in useState hook?
import React, { useState } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
heartsDisplay();
const changeHearts = () => {
heartsNum = heartsNum - 1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App
useEffect(()=>{
heartsDisplay();
},[]);
Call your function inside useEffect() hook
The useEffect Hook allows you to perform side effects in your components.
Some examples of side effects are: fetching data, directly updating the DOM, and timers.
useEffect accepts two arguments. The second argument is optional.
useEffect(<function>, <dependency>)
https://reactjs.org/docs/hooks-effect.html
import React, { useState } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
call the function inside useEffect hook with no deps to run this function one time to trigger when a change in state or props put that state or props in deps array if you want to trigger the function before unmount return a function in useEffect callback do it in that function if you call the function openly in the component function it will call in all render
useEffect(() => {
heartsDisplay();
},[]);
const changeHearts = () => {
heartsNum = heartsNum - 1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App
You are misunderstanding the use of useState. Default value for useState is the default value for the hearts variable.
What you are looking for is probably the useEffect hook.
It's default behavior is
similar to componentDidMount and componentDidUpdate
which basically leads to on page load behavior.
import React, { useState, useEffect } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
useEffect(() => {
heartsDisplay();
},[]);
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
const changeHearts = () => {
heartsNum-=1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<div></div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App

Why does this not print anything on website React.js?

I'm quite new to web development, I am wondering why when I call readContacts() in Contacts.js why the readContacts() function for ContactsContext.js does not output the return to my website? Thank you in advanced.
ContactsContext.js
import React, { useContext } from "react"
import { db } from "../firebase"
import { useAuth } from "./AuthContext"
const ContactsContext = React.createContext()
export function useContacts() {
return useContext(ContactsContext)
}
export function ContactsProvider({ children }) {
const { currentUser } = useAuth()
function createContact(email, firstName, lastName) {
db.ref("users/" + currentUser.uid + "/contacts/" + firstName + " " + lastName).set({
Email: email,
FirstName: firstName,
LastName: lastName
})
}
function readContacts() {
db.ref("users/" + currentUser.uid + "/contacts/").on("value", (snapshot) => {
return (snapshot.val())
})
}
const value = {
createContact,
readContacts
}
return (
<ContactsContext.Provider value={value}>
{children}
</ContactsContext.Provider>
)
}
Contacts.js
import React from "react"
import { ListGroup } from "react-bootstrap"
import { useContacts } from "../contexts/ContactsContext"
export default function Contacts() {
const { readContacts } = useContacts()
return (
<ListGroup variant="flush">
<ListGroup.Item>
{ readContacts() }
</ListGroup.Item>
</ListGroup>
)
}
readContacts function isn't returning anything. But even if you did add return db.ref(..... in the function, you're calling it in the render return. This won't work since React renders are synchronous, pure functions (without side-effects).
Typically you should setup these subscriptions in an useEffect hook and typically you'd update some local state with the snapshot value. Your code is so far from this it's difficult to say what your goal is. My best guess is that you want some contacts state in ContactsProvider and you should be providing this out to the app.
const ContactsContext = React.createContext({
// Don't forget to provide default context value
contacts: [],
createContact: () => {},
});
export function useContacts() {
return useContext(ContactsContext);
}
export function ContactsProvider({ children }) {
const { currentUser } = useAuth();
const [contacts, setContacts] = useState([]); // <-- add state
function createContact(email, firstName, lastName) {
db.ref("users/" + currentUser.uid + "/contacts/" + firstName + " " + lastName).set({
Email: email,
FirstName: firstName,
LastName: lastName
})
}
useEffect(() => {
db.ref("users/" + currentUser.uid + "/contacts/")
.on(
"value",
snapshot => setContacts(snapshot.val()), // <-- update state
);
// NOTE: you likely need to also unsubscribe when component unmounts
}, []);
const value = {
createContact,
contacts, // <-- provide contacts state
}
return (
<ContactsContext.Provider value={value}>
{children}
</ContactsContext.Provider>
)
}
Now context consumers access the contacts array value and render accordingly.
const { contacts } = useContacts();
...
contacts.map(contact => .....

Throttle with lodash isn't throttling using ReactJS

So I have this code
import React, { createRef, useEffect, useCallback, useState } from 'react';
import { throttle } from 'lodash';
import { setProgress } from '../../helpers/markersApi';
const EXECUTE_EVERY_THIRTY_SECONDS = 30 * 1000;
const throttledSetProgress = throttle(setProgress, EXECUTE_EVERY_THIRTY_SECONDS);
const Player = ({}) => {
const updateProgress = (playerPosition, asset, immediateUpdate = false) => {
if (asset.type !== 'EPG_PROGRAM') {
const {
id, episode,
} = asset;
const type = (episode && episode.episodeNumber) ? 'episode' : 'movie';
if (immediateUpdate) {
console.log('IMMEDIATE');
// Cancel possible future invocations and set progress immediately
throttledSetProgress.cancel();
setProgress(id, playerPosition, type);
} else {
throttledSetProgress(id, playerPosition, type);
}
}
};
useEffect(() => {
updateProgress(position, playerAsset);
}, [position, playerAsset]);
}
Problem is the the throttling isn't working since it's running the setProgress every time useEffect is called. Any ideas?
The throttled function should remain the same between re-renderings, meaning we have to use React's UseCallback function. This works by changing throttledSetProgress from this:
const throttledSetProgress = throttle(setProgress, EXECUTE_EVERY_THIRTY_SECONDS);
To this:
const throttledSetProgress = useCallback(
throttle(setProgress, EXECUTE_EVERY_THIRTY_SECONDS),
[],
);
(Don't forget to import useCallback from 'react' as well)

How to get value from useState inside the function

I am trying to build Hanging man game and want to get value from useState inside the checkMatchLetter function, but not sure if that is possible and what I did wrong....
import React, { useState, useEffect } from 'react';
import { fetchButton } from '../actions';
import axios from 'axios';
import 'babel-polyfill';
const App = () => {
const [word, setWord] = useState([]);
const [underscore, setUnderscore] = useState([]);
const [data, setData] = useState([]);
useEffect(() => {
const runEffect = async () => {
const result = await axios('src/api/api.js');
setData(result.data)
}
runEffect();
}, []);
const randomWord = () => {
const chosenWord = data[Math.floor(Math.random() * data.length)];
replaceLetter(chosenWord.word);
}
const replaceLetter = (string) => {
let getString = string; // here it shows a valid string.
setWord(getString);
let stringToUnderScore = getString.replace(/[a-z]/gi, '_');
setUnderscore(stringToUnderScore);
}
useEffect(() => {
const checkLetter = (event) => {
if(event.keyCode >= 65 && event.keyCode <= 90) {
checkMatchLetter(word, String.fromCharCode(event.keyCode).toLowerCase());
}
};
document.addEventListener('keydown', checkLetter);
return () => {
document.removeEventListener('keydown', checkLetter);
}
}, []);
const checkMatchLetter = (keyButton) => {
console.log(keyButton);
let wordLength = word.length;
console.log(wordLength); // here it outputs '0'
/// here I want word of useState here....
}
return (
<div>
<p>{word}</p>
<p>{underscore}</p>
<button onClick={randomWord}></button>
</div>
)
}
export default App;
The reason why I want to obtain that value inside this function is so I can compare the clicked keybutton (a-z) to the current chosenword. And if there is something wrong with other functions, please feel free to share your feedback here below as well.
You're using a variable defined inside the component render function in a useEffect effect and that variable is missing in the hook's deps. Always include the deps you need (I highly recommend the lint rule react-hooks/exhaustive-deps). When you add checkMatchLetter to deps you'll always have the newest instance of the function inside your effect instead of always using the old version from the first render like you do now.
useEffect(() => {
const checkLetter = (event) => {
if(event.keyCode >= 65 && event.keyCode <= 90) {
checkMatchLetter(word, String.fromCharCode(event.keyCode).toLowerCase());
}
};
document.addEventListener('keydown', checkLetter);
return () => {
document.removeEventListener('keydown', checkLetter);
}
}, [checkMatchLetter, word]);
This change will make the effect run on every render. To rectify that, you can memoise your callbacks. However, that's a new can of worms.

Resources