I'm trying to fetch data in react. The problem is i have to click on button twice to get that data.
Although i don't get data on first click it somehow renders if I add JSON.stringify to it. If I don't add JSON.stringify it returns undefined. If anyone know what this is please help me
without clicking
on first click
on second click
import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios'
function Example() {
const [students,setStudents] = useState('')
const [name,setName] = useState('')
const handleClick = async() => {
const data = await axios.get('api/foo')
setStudents(data)
console.log(students)
}
return (
<div className="container">
<h2>Example component</h2>
<button onClick = {handleClick}>Get students</button>
<div>
{JSON.stringify(students.data)}
</div>
</div>
);
}
export default Example;
if (document.getElementById('root')) {
ReactDOM.render(<Example />, document.getElementById('root'));
}
The problem was that setStudents is an asynchronous function, so I just made student object and added to it loading property
const [students,setStudents] = useState({
data: '',
loading: true
})
const [name,setName] = useState('')
const handleClick = async() => {
const data = await axios.get('api/foo')
setStudents({
data: data,
loading: false
})
}
return (
<div className="container">
<h2>Example component</h2>
<button onClick = {handleClick}>Get students</button>
<div>
{students.loading?'':
students.data.data[0].name}
</div>
</div>
);
}
setStudent is an asynchronous function. This means the value of students won't change immediately after you call setStudents.
Try shifting the console.log outside the handleClick function. Like this -
import React, {useState,useEffect} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios'
function Example() {
const [students,setStudents] = useState('')
const [name,setName] = useState('')
const handleClick = async() => {
const data = await axios.get('api/foo')
setStudents(data)
}
console.log(students)
return (
<div className="container">
<h2>Example component</h2>
<button onClick = {handleClick}>Get students</button>
<div>
{JSON.stringify(students.data)}
</div>
</div>
);
}
export default Example;
if (document.getElementById('root')) {
ReactDOM.render(<Example />, document.getElementById('root'));
}
Initially, the value will be an empty string, then it will change to the value from api/foo
React hooks are async so when you are running console.log(students) right after running setStudents(data) it is still not populated, however the 2nd time you click the button it is already populated from the first time you clicked it.
If you want to console the result right after the state setter runs you can see this answer on another question.
Related
I have recently learning react for front end development and have encountered the problem when using useStates. I tried to input a value in a textbox and submit it by clicking a submit button. Normally, only 1 response would be logged on the console, however it appeared twice.
Would be grateful if someone could spot where went wrong in my code snippet.
`
import './App.css';
import { Button } from 'react-bootstrap';
import { useState } from "react";
import axios from 'axios';
function App () {
const [key, setKey] = useState(null);
const [submit, setSubmit] = useState(false);
function getKey(val){
setKey({[val.target.name]: val.target.value})
}
{
console.log(key)
axios
.post('https://jsonplaceholder.typicode.com/posts', key)
.then(response => {
console.log(response)
})
}
return (
<>
<div className = "App">
<h1>Type in the keyword you wish to search: </h1>
<input
type = "text"
name = "keyword"
onChange = {getKey}
/>
<Button onClick = {() => setSubmit(true)} > Submit!</Button>
</div>
</>
);
}
export default App;
`
I am facing a problem with re-rendering after a state change in my NextJS app.
The function sendMessageForm launches a redux action sendMessage which adds the message to the state.
The problem is unrelated to the returned state in the reducer as I am returning a new object(return {...state}) which should trigger the re-render!
Is there anything that might block the re-render ?
This is the file that calls & displays the state, so no other file should be responsible ! But if you believe the problem might lie somewhere else, please do mention !
import { AttachFile, InsertEmoticon, Mic, MoreVert } from '#mui/icons-material';
import { Avatar, CircularProgress, IconButton } from '#mui/material';
import InfiniteScroll from 'react-infinite-scroller';
import Head from 'next/head';
import { useState, useEffect } from 'react';
import Message from '../../components/Message.component';
import styles from '../../styles/Chat.module.css'
import { useRouter } from 'next/router'
import {useSelector, useDispatch} from "react-redux"
import {bindActionCreators} from "redux"
import * as chatActions from "../../state/action-creators/chatActions"
const Chat = () => {
const router = useRouter()
const { roomId } = router.query
const auth = useSelector((state)=> state.auth)
const messages = useSelector((state)=> state.chat[roomId].messages)
const dispatch = useDispatch()
const {getMessages, markAsRead, sendMessage} = bindActionCreators(chatActions, dispatch)
const [inputValue, setInputValue] = useState("")
const sendMessageForm = (e) => {
e.preventDefault()
console.log("***inputValue:", inputValue)
sendMessage(roomId, inputValue)
}
const loadMessages = (page) => {
if(roomId)
getMessages(roomId, page)
}
//user-read-message
useEffect(() => {
//user-read-message
markAsRead(roomId, auth.user._id)
}, [messages]);
return (
<div className={styles.container}>
<Head>
<title>Chat</title>
</Head>
<div className={styles.header}>
<Avatar/>
<div className={styles.headerInformation}>
<h3>Zabre el Ayr</h3>
<p>Last Seen ...</p>
</div>
<div className={styles.headerIcons}>
<IconButton>
<AttachFile/>
</IconButton>
<IconButton>
<MoreVert/>
</IconButton>
</div>
</div>
<div className={styles.chatContainer}>
<InfiniteScroll
isReverse={true}
pageStart={0}
loadMore={loadMessages}
hasMore={messages.hasNextPage || false}
loader={<div className={styles.loader} key={0}><CircularProgress /></div>}
>
{Object.keys(messages.docs).map((key, index)=>{
return<Message
key={index}
sentByMe={messages.docs[key].createdBy === auth.user._id}
message={messages.docs[key].msg}
/>})}
</InfiniteScroll>
<span className={styles.chatContainerEnd}></span>
</div>
<form className={styles.inputContainer}>
<InsertEmoticon/>
<input className={styles.chatInput} value={inputValue} onChange={(e)=>setInputValue(e.target.value)}/>
<button hidden disabled={!inputValue} type='submit' onClick={sendMessageForm}></button>
<Mic/>
</form>
</div>)
};
export default Chat;
useSelector requires a new object with a new reference from the object you are passing to it in order to trigger the re-render
What you're doing with return {...state} is just creating a new object for the parent object but not the nested one useSelector is using, which is in your case :
const messages = useSelector((state)=> state.chat[roomId].messages)
So, you should return the whole state as a new object WITH a new state.chat[roomId].messages object
In other words, the references for the root object & the one being used should be changed.
I am not able to retrieve content from API every time I reload my page it shows error, please see the attached image, I wanted to find the weather details using Weather API and right now I am using static latitude and longitude.
import React, { useState, useEffect } from "react";
import axios from "axios";
import { FaRegSun } from "react-icons/fa";
import "./stylesheets/stylesheets.css";
function WeatherApp1() {
const [weatherData2, setWeatherData2] = useState({});
const API_endpoint2 = `https://api.openweathermap.org/data/2.5/onecall?`;
const API_key = `2a63c27d8ba0b0d14c9e5d59f39ee1ba`;
useEffect(() => {
async function getSecondObject() {
const response = await axios.get(
`${API_endpoint2}lat=28.4360704&lon=77.021184&units=metric&appid=${API_key}`
);
setWeatherData2(response.data);
}
getSecondObject();
}, []);
return (
<div className="mainDiv">
<div className="heading">
<h1>
<FaRegSun /> Weather
</h1>
</div>
{weatherData2.current.temp}
</div>
);
}
export default WeatherApp1;
https://i.stack.imgur.com/oqr7i.jpg
The problem with your code is that you're trying to render {weatherData2.current.temp} before the data is returned from the weather API and that's why your weatherData2 will be undefined while rendering.
You can add a loading state for checking if the data is rendering or already rendered.
You can try below code:
import React, { useState, useEffect } from "react";
import axios from "axios";
import { FaRegSun } from "react-icons/fa";
import "./stylesheets/stylesheets.css";
function WeatherApp1() {
const [loading, setLoading] = useState(true) // Loading state
const [weatherData2, setWeatherData2] = useState({});
const API_endpoint2 = `https://api.openweathermap.org/data/2.5/onecall?`;
const API_key = `2a63c27d8ba0b0d14c9e5d59f39ee1ba`;
useEffect(() => {
async function getSecondObject() {
const response = await axios.get(
`${API_endpoint2}lat=28.4360704&lon=77.021184&units=metric&appid=${API_key}`
);
setWeatherData2(response.data);
setLoading(false) // Setting the loading state to false after data is set.
}
getSecondObject();
}, []);
return (
<div className="mainDiv">
<div className="heading">
<h1>
<FaRegSun /> Weather
</h1>
</div>
{/* Checking for loading state before rendering the data */}
{loading ? (
<p>Loading...</p>
) : (
weatherData2.current.temp
)}
</div>
);
}
export default WeatherApp1;
I have a searchbar component and a Catalog component. The Catalog component contains different cards. Depending on what is typed in the input field of the searchbar component I want to render different cards.
For this to work I need to be able to import the value of the input field into the Catalog component where it is passed in a search function that handles all the rest of the work.
I am able to import the value into my Catalog component but unfortunaty I can't figure out how I can tell if the imported value has changed so I can search again?
I have found some ways to do this with classes but I would like to use hooks instead. I have experimented a bit with "useEffect" but that didn't work out.
Thank you for your help!
This is my code in the searchbar component:
import React, { useState } from 'react';
let input = "";
function Search() {
const [value, setValue] = useState(input);
function onSearch(e) {
setValue(e.target.value);
input = value;
}
return(
<form className="searchForm">
<input className="search" type="text" name="search" autoComplete="off" placeholder="zoeken" value={value} onChange={onSearch}/> </form>
);
}
export { Search, input };
And this is the code in my Catalog
import React, { useState, useEffect } from 'react';
import {input} from "./search";
// other imports
function Catalog(props){
//get cards code and fuse code
const [query, setQuery] = useState(input);
function inputHasChanged(){ //function that can tell if the imported input variable changed
setQuery(input); //update query and rerender cards
}
const results = fuse.search(query)
const searchedCards = query ? results.map(card => card.item) : cards;
//other code
return(
<div>
//render the SearchedCards
</div>
);
}
export {Catalog};
Solution:
code in search:
import React, { useState } from 'react';
const Search = ({ searching }) => {
const [value, setValue] = useState("");
function submit(e){
setValue(e.target.value);
searching(value);
}
return (
<form className="searchForm">
<input
className="search"
type="text" name="search"
autoComplete="off"
placeholder="zoeken"
value={value}
onChange={submit}
/>
</form>
);
};
export { Search };
Search is a child of banner:
import React, {useState, useEffect} from 'react';
import {Search} from './search';
import Header from './Header';
import Overzicht from './Overzicht';
const Banner = ({ search }) => {
const [value, setValue] = useState("");
useEffect(() => {
search(value);
},[search, value]);
return(
<div className="banner">
<Header />
<Search searching={value => setValue(value)} />
<Overzicht />
</div>
);
};
export default Banner;
Banner is a child of home which also contains Catalog:
import React, { useState } from "react";
import Banner from './banner';
import {Catalog} from './Catalog';
function Home(){
const [input, setInput] = useState("");
return(
<div>
<section id="banner">
<Banner search={input => setInput(input)}/>
</section>
<section id="overzicht">
<Catalog search={input} />
</section>
</div>
);
}
export default Home;
And now I can just call
props.search
In Catalog
You can use useEffect as mentioned below:
useEffect(() => {
// Write your logic here
},[input]); // it will run only when the input changes
Push the common state, the query, up to a common ancestor and pass it down as needed to child and descendant components. This way they can "watch" the changes by having new props passed to them.
Below is a simplified version of a structure that would work:
function Catalog({ query }) {
const [results, setResults] = useState(null);
useEffect(() => {
// If `fuse.search` is asynchronous then you might need to debounce
// these queries and/or cancel old queries. If a user types "foo",
// a query is kicked off, and then they finish typing "food", you
// want to cancel the query for "foo" because the results will no
// longer be relevant.
const results = fuse.search(query);
setResults(results);
}, [query])
return (
<div />
);
}
function Search({ query, setQuery }) {
return (
<input onChange={setQuery} value={query} />
)
}
function App() {
const [query, setQuery] = useState("");
return (
<>
<Search query={query} setQuery={setQuery} />
<Catalog query={query} />
</>
);
}
I am writing a notepad web application. I am using React Hooks to use state variables. I am fetching data from an api using axios. Data contains a list of objects containing _id, title, status and detail. I am passing three values to update button as attributes and in onClick() method I am setting the values of my state variables using these attributes. Then these values are sent as props to a UpdateTask component. The probles is, two (_id and title) of those three variables are getting the correct value but one variable (detail) is getting undefined value. following is my code.
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import UpdateTask from './UpdateTask.jsx';
import DeleteTask from './DeleteTask.jsx';
function Tasks()
{
useEffect(()=>
{
async function fetchingData()
{
const tasks = await axios('http://127.0.0.1:8000/tasks');
setTasks(tasks.data)
};
fetchingData();
})
function handleUpdateClick(e)
{
setViewUpdate(!viewUpdate);
setUpdateId(e.target.id);
setUpdateTitle(e.target.title);
setUpdateDetail(e.target.detail);
console.log(e.target)
}
function handleDeleteClick(e)
{
setViewDelete(!viewDelete);
setDeleteId(e.target.id)
}
const [tasks, setTasks] = useState([]);
const [viewUpdate, setViewUpdate] = useState(false);
const [updateId, setUpdateId] = useState(null);
const [updateTitle, setUpdateTitle] = useState('');
const [updateDetail, setUpdateDetail] = useState('');
const [viewDelete, setViewDelete] = useState(false);
const [deleteId, setDeleteId] = useState(null);
var listTasks = tasks.map((task)=>
{
return(
<li className="main-task-list-items task-main" key={task._id} id={task._id}>
<h1>{task.title}</h1>
<p>{task.detail}</p>
<p>Status {task.status.toString()}</p>
<button
className="task-main-btn btn btn-primary"
id={task._id}
detail={task.detail}
title={task.title}
onClick={handleUpdateClick}
>
Update Task
</button>
<button
className="task-main-btn btn btn-danger"
id={task._id}
onClick={handleDeleteClick}
>
Delete Task
</button>
</li>
);
})
return(
<div>
<ul>{listTasks}</ul>
{viewUpdate ? <UpdateTask title={updateTitle} detail={updateDetail} id={updateId} handleCancel={handleUpdateClick} /> : null }
{viewDelete ? <DeleteTask id={deleteId} handleNo={handleDeleteClick}/> : null }
</div>
)
}
export default Tasks;
can anyone help me to solve this?
Try adding onClick by wrapping up with function and pass task -
onClick={ () => handleUpdateClick(task)}
function handleUpdateClick(task) {
setViewUpdate(!viewUpdate);
setUpdateId(task._id);
setUpdateTitle(task.title);
setUpdateDetail(task.detail);
}
Update this in your function call.!
Try this
You are able to get id and title because it comes under eventtarget. Detail is not the property of eventTarget. that might be the issue.