useContext hook update from child component - reactjs

I'm fairly new to hooks as well as useContext, I'm attempting to update onClick from a child component and keep running into the error 'setPokemonId' is not a function, or.. it just doesn't do anything at all.
PokedexContext.js
import React, { useState, useEffect } from "react";
import axios from "axios";
const PokedexContext = React.createContext([{}, () => {}]);
const PokedexProvider = (props) => {
const [state, setState] = useState({});
const [loading, setLoading] = useState(true);
let [error, setError] = useState(false);
let [pokemonId, setPokemonId] = useState(10); //807 max
useEffect(() => {
setLoading(true);
setError(false);
axios
.get(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`)
.then((res) => {
setState(res.data, loading);
setLoading(false);
})
.catch((err) => {
setLoading(false);
if (err.response) {
// pokemon not found
setError("Response");
} else if (err.request) {
// api error
setError("Request");
} else {
// everything else
setError(true);
}
});
}, [pokemonId]);
return (
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId,
]}
>
{props.children}
</PokedexContext.Provider>
);
};
export { PokedexContext, PokedexProvider };
App.js
import React from "react";
import { PokedexProvider } from "../../context/PokedexContext";
import Pokedex from "../Pokedex";
const Landing = () => {
return (
<PokedexProvider>
<main className="container">
<Pokedex />
</main>
</PokedexProvider>
);
};
export default Landing;
Pokedex.js
import React, { useState, useContext } from "react";
import pokedex from "../assets/pokedex.png";
import PokedexScreen from "./PokedexScreen";
import errorHandling from "../utils/errorHandling";
import { PokedexContext } from "../context/PokedexContext";
import spinner from "../assets/pika-load.gif";
const Pokedex = () => {
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
const [power, setPower] = useState(false);
const [shinyDisplay, setShinyDisplay] = useState(false);
console.log(pokemonId);
return (
<div
alt="Pokedex"
data-testid="Pokedex"
className="pokedex"
style={{ backgroundImage: `url(${pokedex})` }}
>
<button className="pokedex--onButton" onClick={() => setPower(!power)}>
{power ? "Off" : "On"}
</button>
{error ? (
<div className="pokedex--screen__error">{errorHandling(error)}</div>
) : power ? (
<>
{loading ? (
<img
className="pokedex--screen__load"
src={spinner}
alt="Loading..."
/>
) : (
""
)}
<button
className="pokedex--shinyButton"
onClick={() => setShinyDisplay(!shinyDisplay)}
>
S
</button>
<span>
<button
className="pokedex--negativeButton"
onClick={() => setPokemonId(pokemonId - 1)}
>
-
</button>
<button
className="pokedex--positiveButton"
// onClick={}
>
+
</button>
</span>
</>
) : (
<div className="pokedex--screen__off" />
)}
</div>
);
};
export default Pokedex;
My goal here is to update the pokemonId when clicking the positive or negative buttons, and is what is currently causing the crashes.

Issue
When you're exporting your values in Context Provider. It's in the 6th index of the array
<PokedexContext.Provider
value={[
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
]}
>
{props.children}
</PokedexContext.Provider>
But you're using from the 4th index in Pokedex.
const [state, setState, error, loading, setPokemonId, pokemonId] = useContext(
PokedexContext
);
Solution
const [
state,
setState,
error,
loading,
setLoading,
pokemonId,
setPokemonId, (6)
] = useContext(
PokedexContext
);

Related

I'm getting this error map is not a function error

This is my code:
import React, { useEffect, useState } from 'react'
import axios from 'axios'
import { useLocation } from 'react-router-dom'
import Button from 'react-bootstrap/esm/Button'
const BookingScreen = () => {
const [room, setRoom] = useState([])
const [loading, setLoading] = useState()
const location = useLocation()
const path = location.pathname.split("/",5)[4]
useEffect(() => {
fetchData()
},[])
const fetchData = async () => {
const res = await axios.get("http://localhost:5000/api/room/" + path)
setRoom(res.data)
console.log(res.data)
setLoading(false)
}
const singleRoom = room.map((item) => {
return (
<div key={item._id} >
<div className="row justify-content-md-center mt-4 ">
<div className="col-md-6">
<img src={item.imageUrl
[0]
} alt="singleRoom" />
</div>
<div className="col-md-4">
<h2>{item.name}</h2>
<p>{item.desc}</p>
<p>Category : {item.categoy}</p>
<Button variant="secondary">
Book Now </Button>
</div>
</div>
</div>
)
})
return (
<>
{loading ? <h1>Loading ...</h1> : singleRoom}
</>
)
}
export default BookingScreen
I'm trying to populate the list singleRoom but it keeps saying map is not a function when I console data I'm getting everything and after that, I used setRoom hook to set the data, but somehow it's empty.
Things i would say, is that if the data returns as an array then its fine, otherwise you'll need to change your logic of return.
import React, { useEffect, useState } from 'react'
import axios from 'axios'
import { useLocation } from 'react-router-dom'
import Button from 'react-bootstrap/esm/Button'
const BookingScreen = () => {
const [room, setRoom] = useState([])
const [loading, setLoading] = useState(true) // set to true by default
const location = useLocation()
useEffect(() => {
fetchData()
}, [])
const fetchData = async () => {
const path = location.pathname.split("/", 5)[4]
setLoading(true)
try {
const res = await axios.get(`http://localhost:5000/api/room/${path}`)
if (res.data) {
setRoom(res.data); // does this actually return an array?
setLoading(false)
} else {
setRoom([]) // force back an empty array if data wasn't returned
setLoading(false)
}
} catch (err) {
setRoom([]) // set back to empty
setLoading(false)
// I'd usually also do an error state here.
}
}
const singleRoom = room.length > 0 ? room.map(({ _id, imageUrl, name, desc, categoy }) => (
<div key={_id} >
<div className="row justify-content-md-center mt-4 ">
<div className="col-md-6">
<img src={imageUrl
[0]
} alt="singleRoom" />
</div>
<div className="col-md-4">
<h2>{name}</h2>
<p>{desc}</p>
<p>Category : {categoy}</p>
<Button variant="secondary">
Book Now </Button>
</div>
</div>
</div>
)
) : <div>No room data found</div>
return (
<>
{loading ? <h1>Loading ...</h1> : singleRoom}
</>
)
}
export default BookingScreen

Set a default content from database in react-draft-wysiwyg editor

I am creating a blog website in which I am embedding react-draft-wysiwyg editor. I am facing problem when the user has to update the blog. When I click the update button the content is gone. I looked into many solutions but I couldn't make it work.
This is my code
import axios from "axios";
import React, { useContext, useEffect, useState } from "react";
import { useLocation } from "react-router";
import { Link } from "react-router-dom";
import { Context } from "../../context/Context";
import "./singlePost.css";
import { EditorState, ContentState, convertFromHTML } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import { convertToHTML } from 'draft-convert';
import DOMPurify from 'dompurify';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import Parser from 'html-react-parser';
export default function SinglePost() {
const location = useLocation();
const path = location.pathname.split("/")[2];
const [post, setPost] = useState({});
const PF = "http://localhost:5000/images/";
const { user } = useContext(Context);
const [title, setTitle] = useState("");
const [desc, setDesc] = useState("");
const [updateMode, setUpdateMode] = useState(false);
useEffect(() => {
const getPost = async () => {
const res = await axios.get("/posts/" + path);
setPost(res.data);
setTitle(res.data.title);
setDesc(res.data.desc);
};
getPost();
}, [path]);
const handleDelete = async () => {
try {
await axios.delete(`/posts/${post._id}`, {
data: { username: user.username },
});
window.location.replace("/");
} catch (err) {}
};
// updating post
const handleUpdate = async () => {
try {
await axios.put(`/posts/${post._id}`, {
username: user.username,
title,
desc,
});
setUpdateMode(false)
} catch (err) {}
};
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
const [convertedContent, setConvertedContent] = useState(null);
const handleEditorChange = (state) => {
setEditorState(state);
convertContentToHTML();
}
const convertContentToHTML = () => {
let currentContentAsHTML = convertToHTML(editorState.getCurrentContent());
setConvertedContent(currentContentAsHTML);
setDesc(currentContentAsHTML);
}
const createMarkup = (html) => {
return {
__html: DOMPurify.sanitize(html)
}
}
return (
<div className="singlePost">
<div className="singlePostWrapper">
{post.photo && (
<img src={PF + post.photo} alt="" className="singlePostImg" />
)}
{updateMode ? (
<input
type="text"
value={title}
className="singlePostTitleInput"
autoFocus
onChange={(e) => setTitle(e.target.value)}
/>
) : (
<h1 className="singlePostTitle">
{title}
{post.username === user?.username && (
<div className="singlePostEdit">
<i
className="singlePostIcon far fa-edit"
onClick={() => setUpdateMode(true)}
></i>
<i
className="singlePostIcon far fa-trash-alt"
onClick={handleDelete}
></i>
</div>
)}
</h1>
)}
<div className="singlePostInfo">
<span className="singlePostAuthor">
Author:
<Link to={`/?user=${post.username}`} className="link">
<b> {post.username}</b>
</Link>
</span>
<span className="singlePostDate">
{new Date(post.createdAt).toDateString()}
</span>
</div>
{updateMode ? (
// <textarea
// className="singlePostDescInput"
// value={desc}
// onChange={(e) => setDesc(e.target.value)}
// />
<Editor
contentState={desc}
editorState={editorState}
onEditorStateChange={handleEditorChange}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
/>
) : (
<p className="singlePostDesc">{Parser(desc)}</p>
)}
{updateMode && (
<button className="singlePostButton" onClick={handleUpdate}>
Update
</button>
)}
</div>
</div>
);
}
I want to display desc which is saved in MongoDB database when the user clicks on update button.
The following part is what I tried to do but didn't work.
const [editorState, setEditorState] = useState(
() => EditorState.createWithContent(
ContentState.createFromBlockArray(
convertFromHTML(desc)
)
),
);
I am getting warning in this:
react.development.js:220 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the r component.
Please help

Destructed props sent to child component returning undefined

I'm a bit lost here. I've done this a bunch of time and have never had this issue before. I'm passing a boolean state to a modal component. I followed the code from the parent and it is set properly but as soon as it gets to the modal it returns as undefined.
Here is the parent:
import React, { useEffect, Fragment, useState } from 'react'
import './styles.css'
import LandingPageModal from './LandingPageModal'
import { testImages } from './testData'
const LandingPage = () => {
const [images, setImages] = useState([])
const [renderImages, setRenderImages] = useState(false)
const [showModal, setShowModal] = useState(false)
const [isLoaded, setIsLoaded] = useState(false)
useEffect(() => {
setImages(testImages)
setShowModal(true)
setIsLoaded(true)
}, [])
useEffect(() => {
if (images && images.length > 0) {
setRenderImages(true)
}
}, [images])
const FeaturedUsers = () => {
return (
renderImages ?
<Fragment>
<div className='grid'>
{images.map((image) => (
<img src={`/images/${image.src}`} alt={image.caption} />
))}
</div>
</Fragment> : ''
)
}
return(
isLoaded ?
<Fragment>
<FeaturedUsers />
<LandingPageModal show={showModal} />
</Fragment> : ''
)
}
export default LandingPage
and here is the modal:
import React, { useState, useEffect } from 'react'
import ReactModal from 'react-modal'
import './styles.css'
const LandingPageModal = ({ showModal }) => {
const [isModalOpen, setIsModalOpen] = useState(showModal)
console.log('Is Show: ' + showModal)
return (
<ReactModal
isOpen={isModalOpen}
>
<div className='main-wrapper'>
<div className='text'>
<p>
<strong>Welcome</strong>
<br />
<br />
Please sign in or sign up
</p>
</div>
</div>
</ReactModal>
)
}
export default LandingPageModal
In the LandingPage component, you accidentally renamed showModal to show.

How to Solve Error in giving variable to the async function?

Iam trying to pass the number of the page in (fetchPlanets) function
put it's gets the following error
here is the code
import React, { useState } from 'react'
import { useQuery } from 'react-query'
import Planet from './Planet'
const fetchPlanets = async (key, page) => {
const res = await fetch(`http://swapi.dev/api/planets/?page=${page}`)
return res.json()
}
const Planets = () => {
const [page, setPage] = useState(1)
const { data, status } = useQuery(['planets', page], fetchPlanets)
return (
<div>
<h2>Planets</h2>
<button onClick={() => setPage(1)}>Page 1</button>
<button onClick={() => setPage(2)}>Page 2</button>
<button onClick={() => setPage(3)}>Page 3</button>
{status === 'loading' && (
<div>Loading Data</div>
)}
{status === 'error' && (
<div>Error Fetching Data</div>
)}
{status === 'success' && (
<div>
{data.results.map(planet => <Planet planet={planet} key={planet.name} />)}
</div>
)}
</div>
)
}
export default Planets
as you can see the variable page in async function is giving value undefined
When using queryKeys, you need to destructure the property from the fetcher function arguments:
const fetcher = ({ queryKey )) => {...};
or
const fetcher = props => {
const { queryKey } = props;
...etc
}
Then you can use the keys based upon their position:
// queryKeys: ["planets", 1]
const [category, page] = queryKeys;
const res = await fetch(`https://swapi.dev/api/${category}/?page=${page}`);
Working example:
Planets.js
import React, { useState } from "react";
import { useQuery } from "react-query";
// import Planet from "./Planet";
const fetchPlanets = async ({ queryKey }) => {
const [category, page] = queryKey;
const res = await fetch(`https://swapi.dev/api/${category}/?page=${page}`);
return res.json();
};
const Planets = () => {
const [page, setPage] = useState(1);
const { data, status } = useQuery(["planets", page], fetchPlanets);
return (
<div className="app">
<h2>Planets</h2>
<button onClick={() => setPage(1)}>Page 1</button>
<button onClick={() => setPage(2)}>Page 2</button>
<button onClick={() => setPage(3)}>Page 3</button>
{status === "loading" && <div>Loading Data</div>}
{status === "error" && <div>Error Fetching Data</div>}
{status === "success" && (
<pre className="code">{JSON.stringify(data.results, null, 4)}</pre>
)}
</div>
);
};
export default Planets;
index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import Planets from "./Planets";
import "./styles.css";
const queryClient = new QueryClient();
ReactDOM.render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<Planets />
</QueryClientProvider>
</StrictMode>,
document.getElementById("root")
);

TypeError: Cannot read property 'map' of undefined in react-hooks

I'm a beginner. Thank you in advance for sharing your knowledge.
This error did not appear originally.
But even though they used the same code, it is now appearing.
What's the reason?
Although the data received through Api has the form of an array, the 'map' method does not work.
I read the other same question but I couldn't solve this problem.
This error bothered me for a day. Let me know what I have to do.
import React, { useState, useEffect } from "react";
import axios from "axios";
import styled from "styled-components";
const MyModal = ({ onClose, selectedItem }) => {
const [data, setData] = useState([]);
let id = selectedItem;
let url = `https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${id}`;
useEffect(() => {
axios
.get(url)
.then((res) => {
setData(res.data.drinks);
})
.catch((error) => {
console.log(error);
});
}, [url]);
return (
<MyModals onClick={onClose}>
<Wrapper>
<button onClick={onClose}>X</button>
{data.map((result) => { 👈 This is the part of the problem.
return (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About>
<Name>{result.strDrink}</Name>
</Container>
);
})}
</Wrapper>
</MyModals>
);
};
export default MyModal;
Likewise, this file has the same problem. Errors appear and then appear.
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import Portal from "../Components/Portal";
import Modal from "../Components/Modal";
const Search = () => {
const [searchTerm, setSearchTerm] = useState("a");
const [cocktails, setCocktails] = useState([]);
const [open, setOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState("");
const handleOpen = (idDrink) => {
setSelectedItem(idDrink);
setOpen(true);
console.log("open");
};
const handleClose = () => {
setOpen(false);
console.log("close");
};
useEffect(() => {
const getDrinks = async () => {
try {
const response = await fetch(
`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${searchTerm}`
);
const data = await response.json();
setCocktails(data);
} catch (error) {
console.log(error);
}
};
getDrinks();
console.log("useEffect");
}, [searchTerm]);
return (
<main style={{ width: "100%" }}>
<SearchForm setSearchTerm={setSearchTerm} />
<Wrapper className="cocktail-list">
{cocktails &&
cocktails.drinks.map(({ idDrink, strDrink, strDrinkThumb }) => (
<Container
className="cocktail"
onClick={() => {
handleOpen(idDrink);
}}
>
<Img>
<img src={`${strDrinkThumb}`} alt={`${strDrink}`} />
</Img>
<Name key={`${idDrink}`}>{`${strDrink}`}</Name>
</Container>
))}
</Wrapper>
{open && (
<Portal>
<Modal selectedItem={`${selectedItem}`} onClose={handleClose} />
</Portal>
)}
</main>
);
};
export default Search;
Is there a problem with the part where I receive the Api data?
import React from "react";
import styled from "styled-components";
import { useState, useEffect } from "react";
import Search from "./Search";
import Modal from "../Components/Modal";
import Portal from "../Components/Portal";
const Main = () => {
const url = "https://www.thecocktaildb.com/api/json/v1/1/random.php";
const [data, setData] = useState([]);
const [open, setOpen] = useState(false);
const [selectedItem, setSelectedItem] = useState("");
useEffect(() => {
const fetchUrl = async () => {
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (error) {
console.log(error);
}
};
console.log("useEffect");
fetchUrl();
}, []);
const handleOpen = (idDrink) => {
setSelectedItem(idDrink);
setOpen(true);
console.log("open");
};
const handleClose = () => {
setOpen(false);
console.log("close");
};
return (
<Wrapper className="main">
{data &&
data.drinks.map(({ idDrink, strDrink, strDrinkThumb }) => (
<>
<Container
onClick={() => {
handleOpen(idDrink);
console.log(handleOpen(idDrink));
}}
>
<img src={`${strDrinkThumb}`} alt={`${strDrink}`} />
<div key={`${idDrink}`}>{`${strDrink}`}</div>
</Container>
{open && (
<Portal>
<Modal selectedItem={`${selectedItem}`} onClose={handleClose} />
</Portal>
)}
</>
))}
<Search />
</Wrapper>
);
};
export default Main;
The error occurred in all files that wrote the 'map' method.
I really don't know what the problem is. Help me!
This has occurred because your map() called before the data come from API. So I will suggest first complete the API call and let the data came properly. Then you should map. You can use-
{data && data.map((result) => { 👈 This will solve ur problem
return (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About>
<Name>{result.strDrink}</Name>
</Container>
);
})}
Make sure of your data from API is a valid Array, you can check it with Array.isArray().
Code could be like this:
{Array.isArray(data) &&
data.map(result => (
<Container>
<Image>
<img src={result.strDrinkThumb} alt={result.idDrink} />
</Image>
<About />
<Name>{result.strDrink}</Name>
</Container>
))}

Resources