Data is not passing as a prop? - javascript

so as you can see below, when I hover over data it shows me the contents of data property (for the first two images) but as you see in the last image, I want to pass the data as a props but it's showing that data as an empty object. So why that's happening?
This is App.js
import React from 'react'
import {Cards,Chart,CountryPicker} from './components';
import styles from './App.module.css';
import { dataApi } from './api'
import image from './images/image.png'
// console.log(dataApi);
class App extends React.Component{
state = {
data : {},
country: '',
}
async componentDidMount () {
const dataFromApi = await dataApi();
this.setState({ data: dataFromApi})
// console.log(data);
// console.log('This is componentDidmount sec')
}
handleCountryChange = async(country) => {
const fetchedData = await dataApi(country);
this.setState({data: fetchedData, country: country})
}
render(){
const {data, country} = this.state
return(
<div className={styles.container}>
<img className={styles.image} src={image} alt="heading"/>
<Cards data = {data}/>
<CountryPicker handleCountryChange={this.handleCountryChange}/>
< Chart data={data} country={country}/>
</div>
);
}
}
export default App;
This Api.js
export const StateData = async(states) =>{
let stateName = 'Nagaland'
if(states){
stateName = states;
console.log("triggered")
}
try{
const response =await fetch(url);
const data = await response.json();
const pop = data.statewise;
const index = pop.findIndex(st => st.state === stateName)
const statedta = {
active :pop[index].active,
confirmed : pop[index].confirmed,
deaths : pop[index].deaths,
recovered: pop[index].recovered
}
return statedta
}catch(error){console.log(error)}
}

The default state value for your data field is an empty object.
This could be your IDE is just showing you the default value for that state field.
I would say that it's best to write a console.log("data:", data) on line 33,
after you destructure the values. Then induce the handleStateChange() method to execute. If the rest of the code is kosher, then you should see that console.log first show an empty object, and later show the expected data.

Related

I lost props after reloading the page in react

I used axios in useEffect of my wrapper component and I sent the data as props to the other component "singleQuestionnaire", in singleQuestionnaire component, I destructured the data, in the first try, it works fine, but after reloading the page it doesn't work with an error : can not read property "map" of undefined
import React, { useEffect, useState } from "react";
import SingleQuestionnaire from "./SingleQuestionnaire";
import { fetchQuestions } from "../../../api/index";
const Questionnaires = ({ match }) => {
const [questions, setQuestions] = useState([]);
const pid = match.params.id;
const getQuestionnaire = async (pid) => {
try {
const { data } = await fetchQuestions(pid);
console.log(data.data, "action in component");
setQuestions(data.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getQuestionnaire(pid);
}, []);
console.log("all questions", questions);
return (
<div>
<SingleQuestionnaire questions={questions} setQuestions={setQuestions} />
</div>
);
};
export default Questionnaires;
and this is my singleQuestionnaire component:
import React, { useEffect, useState } from "react";
const SingleQuestionnaire = ({ questions, setQuestions }) => {
const [questionnaire, setQuestionnaire] = useState([]);
console.log(questions);
const { data } = questions;
console.log("data", data.farmInformationQuestionnaireData);
return <div>simple component</div>;
};
export default SingleQuestionnaire;
For the first time, in console I can see the data "data.data.farmInformationQuestionnaireData". It's an array but for the second time it's undefind.
because questions in SingleQuestionnaire is an empty array before we fetch
which causes an error here
const { data } = questions;
you can add a loading text because initially questions will be an empty array then it will be your res.data (assuming it's an object)
const SingleQuestionnaire = ({ questions, setQuestions }) => {
const [questionnaire, setQuestionnaire] = useState([]);
console.log(questions);
if(questions.length === 0 ) return <h1> Loading</h1>
const { data } = questions;
console.log("data", data.farmInformationQuestionnaireData);
return <div>simple component</div>;
};
it is happening because of the async API call. When you make an async call, the thread does not wait, it moves on and it starts executing other things.
Now your async call might be complete but your callback will not be executed until the stack is empty, that's just how javaScript works. I recommend you use some kind of loader gif or text
{questions ? <SingleQuestionnaire questions={questions} setQuestions={setQuestions} /> : <p>Loading...</p>}

getting TypeError: movieDetails.map is not a function when try to use useState in useEffect

import { useSelector, useDispatch } from "react-redux";
import { useEffect, useState } from "react";
import request from "../../requests";
import { fetchMovies } from "../../feautures/movies/moviesSlice";
import "./SingleMoviePage.scss";
import Rating from "../../components/UI/Rating/Rating";
import axios from "axios";
const SingleMoviePage = ({ match }) => {
const dispatch = useDispatch();
const [movieDetails, setMovieDetails] = useState({})
/* params */
const movieId = match.params.id;
const page = match.params.page;
const genre = match.params.genre;
/* movies reducer handle */
const movies = useSelector((state) => state.movies.movies);
const moviesStatus = useSelector((state) => state.movies.status);
/* movieDetails reducer handle */
/* base urls */
const baseImgUrl = "https://image.tmdb.org/t/p/original";
const movieDetailUrl = `https://api.themoviedb.org/3/movie/${movieId}?api_key=c057c067b76238e7a64d3ba8de37076e&language=en-US`;
useEffect(() => {
const fetchData = async() => {
let response = await axios.get(movieDetailUrl);
response = response.data;
setMovieDetails(response)
}
fetchData()
},[movieDetailUrl])
console.log("data: ",movieDetails )
let content;
if (moviesStatus === "loading") {
<div>Loading ...</div>;
} else if (moviesStatus === "succeeced") {
let movie = movies.find((movie) => movie.id.toString() === movieId);
content = (
<div
className="single-movie__container"
style={{
backgroundImage: `url(${
movie.backdrop_path
? baseImgUrl + movie.backdrop_path
: baseImgUrl + movie.poster_path
})`,
}}
>
<div className="single-movie__information">
<h1 className="single-movie__title">{movie.title}</h1>
<div className="single-movie__rate">
<Rating
rating={movie.vote_average}
className="single-movie__stars"
/>
<div className="single-movie__average">
{movie.vote_average}(Imdb)
</div>
</div>
<p className="single-movie__overview">{movie.overview}</p>
<p className="single-movie__genres">
<label>Genres</label>
{
movieDetails.genres.map(genre => {
console.log("genre: ",genre)
return(
<div>{genre.name}</div>
)
})
}
</p>
</div>
</div>
);
}
useEffect(() => {
if (genre === "POPULAR") {
dispatch(fetchMovies(request.fetchPopular(page)));
} else if (genre === "NOW PLAYING") {
dispatch(fetchMovies(request.fetchNowPlaying(page)));
} else if (genre === "UP COMING") {
dispatch(fetchMovies(request.fetchUpComing(page)));
}
}, [dispatch, genre, page]);
return <div className="single-movie">{content}</div>;
};
export default SingleMoviePage;
I'm trying to make a movie website with react-redux. The issue is when I try to get movie details using useEffect and try to map that in:
<p className="single-movie__genres">
I get TypeError: Cannot read property 'map' of undefined error and I get empty object (data: {}) using console.log("data: ", movieDetails).
But if I refresh the page everything works well and I get
data:
{
adult: false,
backdrop_path: "/6MKr3KgOLmzOP6MSuZERO41Lpkt.jpg",
...
}
using console.log("data: ", movieDetails). Why can't I get data when the page is first loaded?
It is because your initial state does not contain "genres" array inside the object. And when react tries to handle
movieDetails.genres.map(...)
it fall down because movieDetails.genres is undefined (and undefined does not support map method of course). Either include empty array in you initial state like:
const [movieDetails, setMovieDetails] = useState({genres:[]})
or use "?" operator in your chain like:
movieDetails.genres?.map(...)
.map method is a prototype function for type array. you should declare moviedetails as an array like this when setting the default value using useState hook.
const [movieDetails, setMovieDetails] = useState([])
There is a point that i dont understand.As far as i know when the component is first loaded first useEffect worked and filled my movieDetails with datas after that map func worked.I mean js works top to bottom and
shouldn't movieStatus be filled with data until it comes to the map function?

Waiting for async function in React component & Showing Spinner

Beginner here.
Trying to fetch some data from a server and display it in my react component once its fetched.
However, I am having trouble integrating the async function into my react component.
import React, { useState } from "react";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const data = fetchData().catch((error) => console.error(error));
return (
<div>
{data.item.title}
</div>
);
};
export default TestingGraphQL;
I'd like to simply show a spinner or something while waiting, but I tried this & it seems because a promise is returned I cannot do this.
Here you would need to use the useEffect hook to call the API.
The data returned from the API, I am storing here in a state, as well as a loading state to indicate when the call is being made.
Follow along the comments added in between the code below -
CODE
import React, { useState, useEffect } from "react"; // importing useEffect here
import Layout from "#layouts/default";
import ContentContainer from "#components/ContentContainer";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// useEffect with an empty dependency array works the same way as componentDidMount
useEffect(async () => {
try {
// set loading to true before calling API
setLoading(true);
const data = await fetchData();
setData(data);
// switch loading to false after fetch is complete
setLoading(false);
} catch (error) {
// add error handling here
setLoading(false);
console.log(error);
}
}, []);
// return a Spinner when loading is true
if(loading) return (
<span>Loading</span>
);
// data will be null when fetch call fails
if (!data) return (
<span>Data not available</span>
);
// when data is available, title is shown
return (
<Layout>
{data.item.title}
</Layout>
);
};
since fetchData() returns a promise you need to handle it in TestingGraphQL. I recommend onComponentMount do your data call. Setting the data retrieved into the state var, for react to keep track of and re-rendering when your data call is finished.
I added a loading state var. If loading is true, then it shows 'loading' otherwise it shows the data. You can go about changing those to components later to suit your needs.
See the example below, switched from hooks to a class, but you should be able to make it work! :)
class TestingGraphQL extends Component {
constructor() {
super();
this.state = { data: {}, loading: true};
}
//when the component is added to the screen. fetch data
componentDidMount() {
fetchData()
.then(json => { this.setState({ data: json, loading: false }) })
.catch(error => console.error(error));
}
render() {
return (
{this.state.loading ? <div>Loading Spinner here</div> : <div>{this.state.data.item.title}</div>}
);
}
};

How to make a POST request with input text as data React

I am new to react and I am trying to make a POST request using text field data, can anyone help me with how to store that input and make a request after a button is pressed.
I attempted to use useRef() which allowed me to obtain the data however I was not able to store it as a data object to then persist.
Currently my data persists, however it persists an empty object and the state is not being updated.
If anyone can help, I will really appreciate that.
Below is my App.js class
import React, { useState, useEffect, useRef, Component } from 'react';
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists"
});
class App extends Component {
state = {
artists: [],
theArtistName: ""
}
constructor(props){
super(props);
this.getArtists()
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () =>{
let data = await api.get("/").then(({ data }) => data);
this.setState({artists: data}) //setting our artists to be the data we fetch
}
createArtist = async () =>{
let response = await api.post('/', {name: this.state.theArtistName})
console.log(response)
this.getArtists()
}
deleteArtist = async (id) =>{
let data = await api.delete('/${id}')
this.getArtists();
}
handleAddArtist = (event) =>{
event.preventDefault()
this.setState({
theArtistName: event.target.value
})
const data = this.state.theArtistName
console.log(data)
}
componentDidMount(){
this.createArtist()
}
render(){
// const {theArtistName} = this.state
return(
<>
<input type={Text} placeholder="Enter Artist Name" name="theArtistName"></input>
<button onClick={this.createArtist}>Add Artist</button>
{this.state.artists.map(artist => <h4 key={artist.id}>{artist.name}
<button onClick={() =>this.deleteArtist(artist.id)}>Delete artist</button></h4>)}
</>
)
}
}
export default App;
this.setState is an async function, it takes second argument as callback. This should solve your problem. i.e.
import React, { useState, useEffect, useRef, Component } from "react";
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists",
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
artists: [],
theArtistName: "",
};
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () => {
let data = await api.get("/").then(({ data }) => data);
this.setState({ artists: data }); //setting our artists to be the data we fetch
};
createArtist = async () => {
let response = await api.post("/", { name: this.state.theArtistName });
console.log(response);
this.getArtists();
};
deleteArtist = async (id) => {
let data = await api.delete("/${id}");
this.getArtists();
};
handleAddArtist = (event) => {
event.preventDefault();
this.setState(
{
theArtistName: event.target.value,
},
() => {
this.createArtist();
}
);
};
componentDidMount() {
this.getArtists();
}
render() {
// const {theArtistName} = this.state
return (
<>
<input
type={Text}
placeholder="Enter Artist Name"
name="theArtistName"
></input>
<button onClick={this.handleAddArtist}>Add Artist</button>
{this.state.artists.map((artist) => (
<h4 key={artist.id}>
{artist.name}
<button onClick={() => this.deleteArtist(artist.id)}>
Delete artist
</button>
</h4>
))}
</>
);
}
}
export default App;
Let me know if it helps.
because react update state asynchronously so when you are invoking handleAddArtist function which update state the event might be gone so you need to store the value from the event in variable like this :
handleAddArtist = (event) =>{
event.preventDefault()
const {value} = e.target
this.setState({
theArtistName: value
})
}
and to check state update there is a lifecycle method called componentDidUpdate for class component and useEffect for functional component.
[edit]:
call this.createArtist() in componentDidUpdate like this :
componentDidUpdate(prevProps,prevState){
if(prevState.theArtistName!==this.state.theArtistName)
this.createArtist()
}
so the createArtist will fire only when theArtistName state change.
First of all, useRef is a hook only meant for function components and not for class components. For using Refs in class components use React.createRef().
Usually, HTML input elements maintain their own state. The usual way to access the value of an input element from a React component that renders it is to control the input element's state via this component by adding an onChange listener and a value attribute to the input element:
class App extends Component{
constructor(props) {
super(props);
this.state = {artistName: ""};
this.handleArtistNameChange = this.handleArtistNameChange.bind(this);
}
handleArtistNameChange(event) {
this.setState({artistName: event.target.value});
}
render(){
return (
<input
type="text"
value={this.state.artistName}
onChange={this.handleArtistNameChange}
/>
);
}
}
Whenever the value of the input element changes the App component will rerender with the most up-to-date value of the input in its state.
Here is a working example:
You can read more on using form elements in React here.

React Redux - passing arguments to event handler not working

Ok, I got a head scratcher I need a little bit of help with. The setup is that I have React/Redux app with a Categories page that reads a list of categories from an API, then lists them out. That part works fine. What I'm trying to do is pass in an event handler to each of the category child components that, when clicked, dispatches an action that toggles the state of the component, i.e., if the category is selected and clicked on, it will "unselect" it (which actually means deleting an entry from a database table called user_category), and if not selected, will "select" that category for that user (add an entry in the user_category table).
So I've got an onclick handler (handleCatClick) that is supposed to ultimately pass a categoryId and a userId to perform these operations. Unfortunately what I'm finding that even though these arguments are being passed to the function, they end up being undefined. So I'm not sure if I'm passing this function correctly or what exactly I've missed.
Everything works other than this - maybe you can help me spot the problem ;-)
Click here to view the database layout
Click here to see how the category page looks
The applicable pages in my app:
The architecture looks basically like this:
/views/[Categories]
- index.js (wrapper for the Categories Component)
- CategoriesComponent.jsx (should be self-explanatory)
[duck]
- index.js (just imports a couple of files & ties stuff together)
- operations.js (where my handleCatClick() method is)
- types.js (Redux constants)
- actions.js (Redux actions)
- reducers.js (Redux reducers)
[components]
[Category]
- index.jsx (the individual Category component)
/views/index.js(main Category page wrapper)
import { connect } from 'react-redux';
import CategoriesComponent from './CategoriesComponent';
import { categoriesOperations } from './duck'; // operations.js
const mapStateToProps = state => {
// current state properties passed down to LoginComponent (LoginComponent.js)
const { categoryArray } = state.categories;
return { categoryArray }
};
const mapDispatchToProps = (dispatch) => {
// all passed in from LoginOperations (operations.js)
const loadUserCategories = () => dispatch(categoriesOperations.loadUserCategories());
const handleCatClick = () => dispatch(categoriesOperations.handleCatClick());
return {
loadUserCategories,
handleCatClick
}
};
const CategoriesContainer = connect(mapStateToProps,mapDispatchToProps)(CategoriesComponent);
export default CategoriesContainer;
/views/CategoriesComponent.jsx (display layer for the Categories view)
import React from 'react';
import {Row,Col,Container, Form, Button} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import './styles.scss';
import Category from './components/Category';
import shortid from 'shortid';
class CategoriesComponent extends React.Component {
constructor(props) {
super(props);
this.loadUserCats = this.props.loadUserCategories;
this.handleCatClick = this.props.handleCatClick;
}
componentWillMount() {
this.loadUserCats();
}
render() {
return (
<Container fluid className="categories nopadding">
<Row>
<Col xs={12}>
<div className="page-container">
<div className="title-container">
<h4>Pick your favorite categories to contine</h4>
</div>
<div className="content-container">
<div className="category-container">
{
this.props.categoryArray.map((item) => {
return <Category className="category" handleClick={this.props.handleCatClick} key={shortid.generate()} categoryData={item} />
})
}
</div>
</div>
</div>
</Col>
</Row>
</Container>
)
}
}
export default CategoriesComponent
/views/Categories/components/index.jsx (Single Category Component)
import React from 'react';
import {Row,Col,Container, Form, Button} from 'react-bootstrap';
import './styles.scss';
import Img from 'react-image';
class Category extends React.Component {
constructor(props) {
super(props);
this.state = {
categoryName: this.props.categoryData.category_name,
categoryImg: this.props.categoryData.category_img,
categoryId: this.props.categoryData.category_id,
userId: this.props.categoryData.user_id,
selected: this.props.categoryData.user_id !== null,
hoverState: ''
}
this.hover = this.hover.bind(this);
this.hoverOff = this.hoverOff.bind(this);
this.toggleCat = this.toggleCat.bind(this);
}
toggleCat() {
// the onClick handler that is supposed to
// pass categoryId and userId. When I do a
// console.log(categoryId, userId) these two values
// show up no problem...
const {categoryId, userId} = this.state;
this.props.handleClick(categoryId, userId);
}
hover() {
this.setState({
hoverState: 'hover-on'
});
}
hoverOff() {
this.setState({
hoverState: ''
});
}
render() {
const isSelected = (baseCat) => {
if(this.state.selected) {
return baseCat + " selected";
}
return baseCat;
}
return (
<div className={"category" + ' ' + this.state.hoverState} onClick={this.toggleCat} onMouseOver={this.hover} onMouseOut={this.hoverOff}>
<div className={this.state.selected ? "category-img selected" : "category-img"}>
<Img src={"/public/images/category/" + this.state.categoryImg} className="img-fluid" />
</div>
<div className="category-title">
<h5 className={this.state.selected ? "bg-primary" : "bg-secondary"}>{this.state.categoryName}</h5>
</div>
</div>
);
}
}
export default Category;
/views/Categories/duck/operations.js (where I tie it all together)
// operations.js
import fetch from 'cross-fetch';
import Actions from './actions';
import Config from '../../../../config';
const loadCategories = Actions.loadCats;
const selectCat = Actions.selectCat;
const unSelectCat = Actions.unSelectCat;
const localState = JSON.parse(localStorage.getItem('state'));
const userId = localState != null ? localState.userSession.userId : -1;
const loadUserCategories = () => {
return dispatch => {
return fetch(Config.API_ROOT + 'usercategories/' + userId)
.then(response => response.json())
.then(json => {
dispatch(loadCategories(json));
});
}
}
const handleCatClick = (categoryId, categoryUserId) => {
// HERE IS WHERE I'M HAVING A PROBLEM:
// for whatever reason, categoryId and categoryUserId
// are undefined here even though I'm passing in the
// values in the Category component (see 'toggleCat' method)
var params = {
method: categoryUserId !== null ? 'delete' : 'post',
headers: {'Content-Type':'application/json'},
body: JSON.stringify(
{
"category_id": categoryId,
user_id: categoryUserId !== null ? categoryUserId : userId
}
)
};
const toDispatch = categoryUserId !== null ? unSelectCat : selectCat;
return dispatch => {
return fetch(Config.API_ROOT + 'usercategories/', params)
.then(response => response.json())
.then(json => {
dispatch(toDispatch(json));
});
}
}
export default {
loadUserCategories,
handleCatClick
}
The problem that I am having:
So I'm thinking I'm either not referencing handleCatClick correctly, or I'm somehow not passing the categoryId and userId correctly so that when it finally gets to handleCatClick(categoryId, categoryUserId) in operations.js, it ends up as undefined. It's probably something simple but I can't spot it. NOTE: I haven't included files like the types.js or reducers.js, because they seem to be outside the scope of the problem, but if you need them please let me know. Thanks in advance for your help!
Try this changes: Add params to these handlers
const handleCatClick = (categoryId, categoryUserId) => dispatch(categoriesOperations.handleCatClick(categoryId, categoryUserId));
and
return <Category className="category" handleClick={(categoryId, categoryUserId) => this.props.handleCatClick(categoryId, categoryUserId)} key={shortid.generate()} categoryData={item} />

Categories