I've created my backend and it works. I tested different Axios requests in order to create a form.
In my React front project, I created a POST axios request, I console.log(response.data) and I got an object with the id, the title and questions.
I am stuck because I don't know how I could display the data of the object in my front.
Here is my front React code:
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const NewForm = () => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
if (data.length === 0) {
const response = await axios.post(
"https://back-formnest-lereacteur.herokuapp.com/form/create",
{
title: "Your event",
}
);
console.log(response.data);
setData(response.data);
}
};
fetchData();
}, [data]);
return (
I am completely stuck here to display the data of my backend in my front
This is my backend code:
const express = require("express");
const router = express.Router();
const Form = require("../models/Form");
router.post("/form/create", async (req, res) => {
try {
if (req.fields.title) {
const newForm = new Form({
title: req.fields.title,
});
await newForm.save();
return res.json(newForm);
} else {
return res.status(400).json({ error: "Missing parameters" });
}
} catch (e) {
return res.status(400).json({ error: e.message });
}
});
This is my console.log(response.data) I want to display in my front React page:
I edited my code and I got an error:
import React, { useState, useEffect } from "react";
/* import { Link } from "react-router-dom"; */
import axios from "axios";
const NewForm = () => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
if (data.length === 0) {
const response = await axios.post(
"https://back.herokuapp.com/form/create",
{
title: "Nouveau formulaire",
}
);
console.log(response.data);
setData(response.data);
}
};
fetchData();
}, [data]);
return (
<>
<div>My forms</div>
<div>
{data && (
<>
<p>{data.title}</p>
{data.questions.map((question, index) => (
<div> {question} </div>
))}
</>
)}
</div>
</>
);
};
export default NewForm;
Hi Guys,
I updated my code but I have still an error code (TypeError: Cannot read property 'length' of undefined)
<>
<div>My forms</div>
<div>
{data && (
<>
<p>{data.title}</p>
{data.questions.length &
data.questions.map((question, index) => {
return <p key={index}>{question}</p>;
})}
</>
)}
</div>
</>
I updated again my code, I succeeded only to display the title of my form but I did not succeed to display the data included in my question array. I have a "0" which appears instead of my data. Please help
return (
<>
<div>My forms </div>
<div>
{data && data.questions && (
<>
<div>{data.title} </div>
{data.questions.length &
data.questions.map((question, index) => {
return <p key={index}>{question}</p>;
})}
</>
)}
</div>
</>
I updated again, same error appears:
return (
<>
<div>My forms </div>
<div>
{data &&
data.questions &&
data.questions.length(
<>
<div>{data.title} </div>
{data.questions.map((question, index) => {
return <p key={index}>{question}</p>;
})}
</>
)}
</div>
you've done the hard part!
now just .map over the question array if you want to display them out?
<div>
{data.questions.map((question => (
<div> {question.title} </div>
))}
</div>
I've only done a simple example but of course you can display as much or as little as you want
of course anything in state you can render. so if you want to display title do:
{data.title} wherever pleases you
It looks like your backend responds with an object, so here is how you could go about it.
1) Change your initinal state to undefined like this.
const [data, setData] = useState([]);
to
const [data, setData] = useState(undefined);
Then you can use it in the display like this
return (
<div>
{data && (
<>
<p>{data._id}</p>
<p>{data.title}</p>
{data.question.length && data.question.map((question,idx) => {
// this is assuming that each question is just a string and not an object
return (<p key={idx}>{question}</p>)
})}
</>
)}
</div>
)
Related
so i'm trying to implement a custom react hook for fetch. It's working fine, but i can't seem to do it with the errors. if i try to display the error in a custom component it's says object is not a valid React child ... okey i know that, but how then it's working when there's no error in the componenet ? Here's the code:
Hook:
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal: signal })
.then(res => res.json())
.then(result => {
setData([result]);
})
.catch(err => {
setError(true);
setErrorMessage(err);
})
return () => {
setError(false);
controller.abort();
};
}, [url]);
return { data, error, errorMessage };
};
export default useFetch;
Component:
const WeatherNow = () => {
const { city } = useContext(CityContext);
import ErrorHandler from '../error-component/ErrorHandler';
const { data, error, errorMessage } = useFetch(`https://api.weatherapi.com/v1/forecast.json?key=${process.env.REACT_APP_API_KEY}&q=${city}&aqi=no`);
if (error) {
return <>
<ErrorHandler props={errorMessage} />
</>
};
return (
<>
{data && data.map(x => (
<div className="today-forecast" key={city}>
<h4>
{city}
</h4>
<div>
{x.current.condition.text}
</div>
<div>
<img src={x.current.condition.icon} alt='' />
</div>
<h3>
{x.current.feelslike_c} *C
</h3>
<h5 className='rain-wind'>
Rain: {x.current.precip_in} % / Wind: {x.current.wind_kph} km/h
</h5>
<div className='links'>
<Link to='/hourly'>Hourly</Link> <Link to='/daily'>Daily</Link>
</div>
</div>
))}
</>
);
};
The ErrorHandler:
import './ErrorHandler.css';
import error from './error.png';
const ErrorHandler = ({ props }) => {
return (
<div className="error-component">
<div>
<h4>
{props}
</h4>
</div>
<div>
<img src={error} />
</div>
</div>
);
};
export default ErrorHandler;
Because of the catch (err) is an unknown type, it might return anything and more likely an object with a message key.
Try to change the way you are setting the error message and make sure it’s a string:
setErrorMessage(typeof err?.message === "string" ? err.message : "Unknown Error");
Warning
Using process.env.REACT_APP_API_KEY in client side is not safe at all.
The problem is in the catch block , somehow the err is inpromise and i can't use it
I am trying to display a photo from an object I received from an API. I was able to get down to the data information, but the image is not displaying in the displayPhoto function. I was wondering what was wrong with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
// fetch api
// display data
// display more data
export default function App() {
const [data, setData] = useState([]);
const fetchAPI = () => {
axios.get('https://randomuser.me/api')
.then(res => {
// handle success
console.log(res["data"]["results"]);
const indData = (res["data"]["results"]);
const updatedData = [
...data,
...indData
]
setData(updatedData);
// console.log(data)
})
.catch(error => {
// handle error
console.log(error);
})
}
const mapData = (info) => {
const first = (info["name"]["first"])
const last = (info["name"]["last"])
return `${first} ${last}`;
}
const displayPhoto = (info) => {
const picture = (info["picture"]["large"]);
return <img src={picture} />;
}
return (
<>
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={fetchAPI} >Fetch API </button>
{data.map((e, idx) => (
<p>{mapData(e, idx)}</p>
{displayPhoto(e)}
))}
</div>
</>
);
}
It looks like you put the wrong src to img tag in displayPhoto function. You should check the path and then render img component with it.
{data.map((e, idx) => (
<div>
<p>{mapData(data, idx)}</p>
<img src={data.rightSrcl} />
</div>
))}
What would be the best way to handle errors and display them in a React App using Hooks, at the moment if I try to break the app by mistyping the URL it shows the error but still the data sometimes also, however if I update the state to an empty array in the catch block setData([]);, then it works fine, I just wanted to check and see if this is the ideal way or is there another way?
App.js
import React, {useEffect} from 'react';
import './App.css';
import axios from 'axios';
const App = () => {
interface DataHolder {
userId: string;
id: string;
title: string;
body: string;
}
const [data, setData] = React.useState<DataHolder[]>([]);
const [isLoading, setIsLoading] = React.useState<Boolean>(false);
const [hasError, setHasError] = React.useState<Boolean>(false)
useEffect( () => {
const fetchData = async (): Promise<any> => {
setIsLoading(true);
setHasError(false);
try {
const result = await axios('https://jsonplaceholder.typicode.com/posts');
setData(result.data);
} catch (err) {
setHasError(true);
setData([]);
console.log(err);
}
setIsLoading(false);
}
fetchData()
return () => {
console.log('cleaned');
}
}, [setData]);
return (
<>
{hasError && <p >Something went wrong. problem with the data feed.</p>}
{isLoading ? (
<p >Loading ...</p>
) : (
<ul>
{data.map(item => (
<li key={item.id} >
<p>{item.title}</p>
</li>
))}
</ul>
)}
</>
);
}
export default App;
Conditional rendering should help when you are dealing with hooks.
Loading
Error
Data display part
You can order like this.
if (isLoading) {
return <p>Loading ...</p>;
}
if (hasError) {
return <p>Something went wrong. problem with the data feed.</p>;
}
return (
<>
<ul>
{data?.map((item) => (
<li key={item.id}>
<p>{item.title}</p>
</li>
))}
</ul>
</>
);
You can also give a condition like this, checking the length of the data
return (
<>
<ul>
{data.length > 0 ? data.map((item) => (
<li key={item.id}>
<p>{item.title}</p>
</li>
)) : null}
</ul>
</>
);
I have dynamic routes based on search results. How do I go back and see my previously rendered search results & search term in input field versus and empty Search page?
I've started looking into useHistory/useLocation hooks, but I'm lost.
1. Search page
export default function Search() {
const [searchValue, setSearchValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [noResults, setNoResults] = useState(false);
const [data, setData] = useState([]);
const fetchData = async () => {
const res = await fetch(
`https://api.themoviedb.org/3/search/movie?api_key={API_KEY}&query=${searchValue}`
);
const data = await res.json();
const results = data.results;
if (results.length === 0) setNoResults(true);
setData(results);
setIsLoading(false);
};
function handleSubmit(e) {
e.preventDefault();
setIsLoading(true);
fetchData();
// setSearchValue("");
}
return (
<div className="wrapper">
<form className="form" onSubmit={handleSubmit}>
<input
placeholder="Search by title, character, or genre"
className="input"
value={searchValue}
onChange={(e) => {
setSearchValue(e.target.value);
}}
/>
</form>
<div className="page">
<h1 className="pageTitle">Explore</h1>
{isLoading ? (
<h1>Loading...</h1>
) : (
<div className="results">
{!noResults ? (
data.map((movie) => (
<Result
poster_path={movie.poster_path}
alt={movie.title}
key={movie.id}
id={movie.id}
title={movie.title}
overview={movie.overview}
release_date={movie.release_date}
genre_ids={movie.genre_ids}
/>
))
) : (
<div>
<h1 className="noResults">
No results found for <em>"{searchValue}"</em>
</h1>
<h1>Please try again.</h1>
</div>
)}
</div>
)}
</div>
</div>
);
}
2. Renders Result components
export default function Result(props) {
const { poster_path: poster, alt, id } = props;
return (
<div className="result">
<Link
to={{
pathname: `/results/${id}`,
state: { ...props },
}}
>
<img
src={
poster
? `https://image.tmdb.org/t/p/original/${poster}`
: "https://www.genius100visions.com/wp-content/uploads/2017/09/placeholder-vertical.jpg"
}
alt={alt}
/>
</Link>
</div>
);
}
3. Clicking a result brings you to a dynamic page for that result.
export default function ResultPage(props) {
const [genreNames, setGenreNames] = useState([]);
const {
poster_path: poster,
overview,
title,
alt,
release_date,
genre_ids: genres,
} = props.location.state;
const date = release_date.substr(0, release_date.indexOf("-"));
useEffect(() => {
const fetchGenres = async () => {
const res = await fetch(
"https://api.themoviedb.org/3/genre/movie/list?api_key={API_KEY}"
);
const data = await res.json();
const apiGenres = data.genres;
const filtered = [];
apiGenres.map((res) => {
if (genres.includes(res.id)) {
filtered.push(res.name);
}
return filtered;
});
setGenreNames(filtered);
};
fetchGenres();
}, [genres]);
return (
<div className="resultPage">
<img
className="posterBackground"
src={
poster
? `https://image.tmdb.org/t/p/original/${poster}`
: "https://www.genius100visions.com/wp-content/uploads/2017/09/placeholder-vertical.jpg"
}
alt={alt}
/>
<div className="resultBackground">
<div className="resultInfo">
<h1> {title} </h1>
</div>
</div>
</div>
);
}
4. How do I go back and see my last search results?
I'm not sure how to implement useHistory/useLocation with dynamic routes. The stuff I find online mentions building a button to click and go to last page, but I don't have a button that has to be clicked. What is someone just swipes back on their trackpad?
One way you could do this would be to persist the local component state to localStorage upon updates, and when the component mounts read out from localStorage to populate/repopulate state.
Use an useEffect hook to persist the data and searchValue to localStorage, when either updates.
useEffect(() => {
localStorage.setItem('searchValue', JSON.stringify(searchValue));
}, [searchValue]);
useEffect(() => {
localStorage.setItem('searchData', JSON.stringify(data));
}, [data]);
Use an initializer function to initialize state when mounting.
const initializeSearchValue = () => {
return JSON.parse(localStorage.getItem('searchValue')) ?? '';
};
const initializeSearchData = () => {
return JSON.parse(localStorage.getItem('searchData')) ?? [];
};
const [searchValue, setSearchValue] = useState(initializeSearchValue());
const [data, setData] = useState(initializeSearchData());
I'm fetching two things. An item by id and then the item comments by item id. I when npm start I get
TypeError: data.comments is undefined
But if I comment out
<Comment data={itemComments} />
And then run npm start, the item data loads and if I uncomment the comment tag after the item data has already loaded comments shows until I refresh or reload again, it's only when I try to load them simultaneously I get the error.
Item.js
import React, { useEffect, useState } from "react";
import Comment from "./Comment";
import axios from "axios";
const Item = () => {
const itemId = "6019afbce548e33e7c2f4e56";
const [item, setItem] = useState([]);
const [itemComments, setItemComments] = useState([]);
const fetchData = () => {
const item = `http://localhost:3000/api/v1/items/${itemId}`;
const itemComments = `http://localhost:3000/api/v1/items/${itemId}/comments`;
const getItem = axios.get(item);
const getItemComments = axios.get(itemComments);
axios.all([getItem, getItemComments]).then(
axios.spread((...allData) => {
const allItemData = allData[0].data;
const allItemCommentsData = allData[1].data;
setItem(allItemData);
setItemComments(allItemCommentsData);
})
);
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
<div>
<div>
<h3>{item.title}</h3>
</div>
<div>
<p>Price</p>
<p>${item.price}</p>
</div>
<div>
<h3>Offers & Comments</h3>
<Comment data={itemComments} />
</div>
</div>
</div>
);
};
export default Item;
ItemComments.js
import React from "react";
const Message = (props) => {
const { data } = props;
console.log(data);
return (
<>
{data &&
data.comments.map((comment, i) => (
<div key={i}>
<div>
<div>
<p>{comment.comment}</p>
</div>
</div>
</div>
))}
</>
);
};
export default Message;
After first render react try to access comments inside itemComments when its just an empty array, and you just check if its not undefined in your children component:
{data && data.comments.map((comment, i) => (
<div key={i}>
<div>
<div>
<p>{comment.comment}</p>
</div>
</div>
</div>
))}
so change your initial state to this:
const [itemComments, setItemComments] = useState({comments:[]});
name your comment state by its initial name like this "useState({comments:[]})" and make sure that data.comment is not empty and also try to make fetch data asynchronously and let me know the result, please
const fetchData = async () => {
const item = `http://localhost:3000/api/v1/items/${itemId}`;
const itemComments = `http://localhost:3000/api/v1/items/${itemId}/comments`;
const getItem = await axios.get(item);
const getItemComments = await axios.get(itemComments);
const allData=await axios.all([getItem, getItemComments])
const allItemData= await axios.spread((...allData) => allData[0].data)
const allItemCommentsData= await axios.spread((...allData) => allData[1].data)
setItem(allItemData);
setItemComments(allItemCommentsData);
};