The error is, TypeError: Cannot read properties of undefined (reading 'results') please help me i want to finish the project. I have used API from movie database inorder to fetch data of the movies. but in so doing i get the errors, you cannot see them in the code editor, i use visual studio code but when i save, it doesn't show the results and it has challenged me. I am going to quit the project because i can no longer go further more. I need some help. Yes i can understand that i am new in this react but i have practiced it for now 3 months.
I have not posted other codes but if in need i will have to post the whole project. Some help please and i will appriciate.
Home.js:27 Uncaught TypeError: Cannot read properties of undefined (reading 'results')
at Home (Home.js:27)
at renderWithHooks (react-dom.development.js:14985)
at mountIndeterminateComponent (react-dom.development.js:17811)
at beginWork (react-dom.development.js:19049)
home.js
import React, { useState, useEffect } from "react"; //rafce
import reactDom from "react-dom";
//Config
import { POSTER_SIZE, BACKDROP_SIZE, IMAGE_BASE_URL } from "../config";
//components
import HeroImage from "./HeroImage";
//hook
import { useHomeFetch } from "../hooks/useHomeFetch";
//image
import NoImage from "../images/no_image.jpg";
import Grid from "./Grid";
import Thumb from "./Thumb";
import Spinner from "./Spinner";
import SearchBar from "./SearchBar";
const Home = () => {
const { state, loading, error, setSearchTerm } = useHomeFetch();
console.log(state);
return (
<>
{state.results ? (
<HeroImage
image={`${IMAGE_BASE_URL}${BACKDROP_SIZE}${state.results[0].backdrop_path}`}
title={state.results[0].original_title}
text={state.results[0].overview}
/>
) : null}
<SearchBar setSearchTerm={setSearchTerm} />
<Grid header="Popular Movies">
{state.results.map((movie) => (
<Thumb
key={movie.id}
clickable
image={
movie.poster_path
? IMAGE_BASE_URL + POSTER_SIZE + movie.poster_path
: NoImage
}
movieId={movie.id}
/>
))}
</Grid>
<Spinner />
</>
);
};
export default Home;
useHomeFetch.js
import { useState, useEffect, useRef } from "react";
import reactDom from "react-dom";
//API
import API from "../API";
//initial state
const initialState = {
page: 0,
results: [],
total_pages: 0,
total_results: 0,
};
export const useHomeFetch = () => {
const [searchTerm, setSearchTerm] = useState("");
const [state, setState] = useState();
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const fetchMovies = async (page, searchTerm = "") => {
try {
setError(false);
setLoading(true);
const movies = await API.fetchMovies(searchTerm, page);
setState((prev) => ({
...movies,
results:
page > 1 && prev ? [...prev.results, ...movies.results] : [...movies.results],
}));
} catch (error) {
setError(true);
}
setLoading(false);
};
//initial ans search
useEffect(() => {
// setState(initialState);
fetchMovies(1);
}, []);
return { state, loading, error, setSearchTerm };
};
The first time when you setState the prev is undefined, but you don't have check for that
Try something like that:
results: page > 1 && prev ? [...prev.results, ...movies.results] : [...movies.results]
you are missing the 'movies' after prev. it should be:
page > 1 && prev ? [...prev.movies.results, ...movies.results] : [...movies.results],
Related
I'm in the process of building a merch e-commerce website for a client utilizing the commerce.js API however I've run into a problem. When passing the "cart" object as a prop to the checkout file it returns as an empty object which breaks the website. The web application passes the "cart" object as a prop in other parts of the code and works just fine. Is there something I'm doing wrong?
Code for reference:
import React, { useState, useEffect } from 'react';
import {Paper, Stepper, Step, StepLabel, Typography, CircularProgress, Divider, Button} from '#material-ui/core';
import { commerce } from '../../../lib/commerce';
import Addressform from '../Addressform';
import Paymentform from '../Paymentform';
const steps =['Shipping Address', 'Payment details'];
const Checkout = ({ cart }) => {
const [activeStep, setActiveStep] = useState(0);
const [checkoutToken, setCheckoutToken] = useState(null);
useEffect (() => {
const generateToken = async () => {
console.log(cart.id);
// returns as undefined
try {
const token = await commerce.checkout.generateToken(cart.id, { type: 'cart' });
console.log(token);
setCheckoutToken(token);
console.log("Success!")
} catch (error) {
console.log(error); //Returns 404 Error Obv
console.log("Didnt work")
}
}
generateToken();
}, []);
const Confirmation = () => (
<>
Confirmation
</>
);
const Form = () => activeStep === 0
? <Addressform />
: < Paymentform />
return(
<>
...
</>
);
};
export default Checkout;
I can't render props from useContext because it returns undefined before desired object. I can't seem to find any solution to my problem.
This is the child element I'm trying to render:
const Reviews = ({reviews}) => {
return (
<div className={styles['review__card']}>
{reviews.map((review) => {
return(
<div className={styles['review__card__item']}>
<div className={styles['card__item__meta']}>
<span>{review.name}</span>
<span><StarRating rating={review.rating}/></span>
</div>
<div className={styles['card__item__p']}>
<p>{review.revew}</p>
</div>
</div>
)})}
</div>
)
}
export default Reviews
This is Parent Element:
import React, { useContext, useEffect } from 'react'
import { useParams } from 'react-router-dom'
import { RestaurantsContext } from '../context/RestaurantsContext';
import Wrapper from '../components/Wrapper'
import Header from '../components/Header'
import Reviews from '../components/Reviews'
import AddReview from '../components/AddReview'
import RestaurantFinder from '../apis/RestaurantFinder';
const RestaurantDetailPage = () => {
const {id} = useParams()
const {selectedRestaurant, setSelectedRestaurant} = useContext(RestaurantsContext)
useEffect(()=> {
const fetchDate = async () => {
try {
const response = await RestaurantFinder.get(`/${id}`)
setSelectedRestaurant(response.data.data)
}
catch (err) {console.log(err)
}
}
fetchDate()
}, [])
console.log(selectedRestaurant.reviews)
return (
<Wrapper>
<Header title={ 'RestaurantDetailPage' }/>
<div>{selectedRestaurant && (
<>
<Reviews reviews={selectedRestaurant.reviews}/>
<AddReview/>
</>
)}</div>
</Wrapper>
)
}
export default RestaurantDetailPage
Whenever I console.log(selectedRestaurant.reviews) it gives me undefined and then it gives me object query. I assume that .map() is getting an error because it is trying to render that first argument which is undefined
Here is Context js
import React, {useState, createContext} from 'react';
export const RestaurantsContext = createContext();
export const RestaurantsContextProvider = props => {
const [restaurants, setRestaurants] = useState([])
const [selectedRestaurant, setSelectedRestaurant] = useState([])
const addRestaurants = (restaurant) => {
setRestaurants([...restaurants, restaurant]);
}
return (
<RestaurantsContext.Provider value={{restaurants, setRestaurants, addRestaurants, selectedRestaurant, setSelectedRestaurant }}>
{props.children}
</RestaurantsContext.Provider>
)
}
I have found the issue and solved it though I'm not entirely sure how it works.
const [selectedRestaurant, setSelectedRestaurant] = useState([])
default useState value should be null using useState(null)
I'm supposed to have a modal appear with an image in it. There are next and previous buttons which controls which image you are currently viewing. The modal is rendered in a portal. That in itself is working correctly. However, when I add children, and those childrens are updated, the modal only (not the portal) gets removed from the flow. In the React DevTools, the "isOpen" state of the modal is still set to true. I am using React 17.0.2 with NextJS 12.0.4 and Styled Components 5.3.3.
I have tried:
memoizing my components (as you can see there are some remnants of those trials) but this did not work
extracting the state of the modal to the parent and passing it as props and it didn't work either
I know there must be something wrong that I'm doing here so if you could help me find it that would be much appreciated!
Here is the controller where the modal is rendered:
import { FC, MouseEventHandler, useEffect, useState } from "react";
import { Photo } from "services/Images/Images.interfaces";
import { useGetNextPhoto, useGetPhotos, useGetPreviousPhoto } from "state";
import SlideshowContextProvider from "./Context/SlideshowContext";
import SlideShowModal from "./SlideShowModal";
const SlideshowController: FC = () => {
const photos = useGetPhotos();
const [currentlyViewedPhoto, setCurrentlyViewedPhoto] = useState<Photo | null>(null);
const nextPhoto = useGetNextPhoto(currentlyViewedPhoto?.id);
const previousPhoto = useGetPreviousPhoto(currentlyViewedPhoto?.id);
const onPreviousRequest: MouseEventHandler<HTMLButtonElement> = (event) => {
event.preventDefault();
setCurrentlyViewedPhoto(previousPhoto);
};
const onNextRequest: MouseEventHandler<HTMLButtonElement> = async (event) => {
event.preventDefault();
setCurrentlyViewedPhoto(nextPhoto);
};
useEffect(() => {
setCurrentlyViewedPhoto(photos[0]);
}, [photos]);
return (
<SlideshowContextProvider
currentlyViewing={currentlyViewedPhoto}
onNextSlideRequest={onNextRequest}
onPreviousSlideRequest={onPreviousRequest}
>
<SlideShowModal />
</SlideshowContextProvider>
);
};
export default SlideshowController;
The SlideshowModal:
import { Modal } from "components";
import { FC } from "react";
import SlideshowControlBar from "./SlideshowControlBar";
import SlideshowImage from "./SlideshowImage";
const SlideShowModal: FC = () => {
return (
<Modal uniqueKey="slideshow">
<SlideshowImage />
<SlideshowControlBar />
</Modal>
);
};
export default SlideShowModal;
The modal in itself:
import Portal from "components/Portal/Portal";
import { FC, useEffect, useMemo, useState } from "react";
import { useRegisterModal } from "state";
import styled from "styled-components";
import useWindowScrollLock from "./hook/UseWindowScrollLock";
interface Props {
uniqueKey: string;
isBackgroundOpaque?: boolean;
}
... Styled elements
const Modal: FC<Props> = ({ uniqueKey, isBackgroundOpaque = true, children }) => {
const [isOpen, setIsOpen] = useState(false);
const open = () => setIsOpen(true);
const close = () => setIsOpen(false);
const register = useRegisterModal(uniqueKey);
const isModalOpen = useMemo(() => isOpen, [isOpen]);
useEffect(() => {
register({ open, close });
}, [register]);
useWindowScrollLock(isModalOpen);
return isModalOpen ? (
<Portal>
<Container>
<InnerModal>
<Close onClick={close}>X</Close>
{children}
</InnerModal>
</Container>
<Background onClick={close} opaque={isBackgroundOpaque} />
</Portal>
) : null;
};
export default Modal;
I have read questions with similar titles and they have not solved my problem.
I have an API call whose result needs to be shared amongst several components. The parent component makes the call and React's context is used to share it amongst the child components:
MainPage.js:
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { VideoPlayer } from "../components/VideoPlayer";
import VideoContext from "../components/VideoContext";
export default function Watch() {
const [video, setVideo] = useState({});
const { videoHash } = useParams();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
setIsLoading(true);
getVideo();
}, [videoHash]);
const getVideo = async () => {
if(videoHash) {
const res = await getVideoFromApi();
setIsLoading(false);
// The first time this runs nothing happens, the video can't be played
// The second time it runs (i.e. when the URL/videoHash changes) it updates
// but it shows the video from the first run
setVideo(res.video);
}
};
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (
<VideoContext.Provider value={{ video, setVideo }}>
<VideoPlayer videoHash={videoHash} />
</VideoContext.Provider>
)}
</>
);
}
VideoPlayer.js:
import React, { useState, useEffect, useContext } from "react";
import VideoContext from "./VideoContext";
import styles from "./VideoPlayer.module.css";
export function VideoPlayer({ videoHash }) {
const { video, setVideo } = useContext(VideoContext);
const [forceRender, setforceRender] = useState(true);
useEffect(() => {
// I tried adding this to no effect
setforceRender(!forceRender);
}, [videoHash]);
return (
<video controls className={styles["video-player"]}>
<source src={video.VideoUrl} type="video/mp4" />
Sorry, your browser does not support embedded videos.
</video>
);
}
VideoContext.js:
import { createContext } from "react";
export default createContext({
video: {},
setVideo: () => {}
});
It works when the page loads, but when my Link components change the videoHash property the new video loads (I can see when I console.log() the API call) but it does not update in the video player.
The second time a link is clicked and the videoHash param is changed, the video displays but it's for the previous video.
https://codesandbox.io/s/blazing-lake-k4i8n?file=/src/VideoPlayer.js
Unless I'm missing something, I think the VideoPlayer would be all right just behaving as a functional component without any hooks for state, that could be handled by Watch. When you click on a link to another route that will point to watch, the videoHash will change
VideoPlayer.js
import React from "react";
import { Link } from "react-router-dom";
export function VideoPlayer({ videoHash }) {
// const { video, setVideo } = useContext(VideoContext);
// const [forceRender, setforceRender] = useState(true);
// useEffect(() => {
// // I tried adding this to no effect
// setforceRender(!forceRender);
// }, [videoHash]);
// console.log(video);
// Am I missi
return (
<div>
Am I missing something or could you just use your videoHash: {videoHash},
here?
<Link to="/watch/a">Previous</Link>
<Link to="/watch/c">Next</Link>
</div>
);
}
Watch.js
import React, { useState, useEffect, useCallback } from "react";
import { useParams } from "react-router-dom";
import { VideoPlayer } from "./VideoPlayer";
import VideoContext from "./VideoContext";
export default function Watch() {
const [video, setVideo] = useState({});
const { videoHash } = useParams();
const [isLoading, setIsLoading] = useState(true);
const getVideo = useCallback(async () => {
if (videoHash) {
const res = await getVideoFromApi();
setTimeout(() => {
setIsLoading(false);
setVideo(res);
}, 1000);
}
}, [videoHash]);
useEffect(() => {
setIsLoading(true);
getVideo();
}, [getVideo]);
const getVideoFromApi = async () => {
const videoArray = ["A", "B", "C"];
const randomItem =
videoArray[Math.floor(Math.random() * videoArray.length)];
return Promise.resolve(randomItem);
};
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (
<VideoContext.Provider value={{ video, setVideo }}>
<VideoPlayer videoHash={videoHash} />
</VideoContext.Provider>
)}
</>
);
}
VideoContext.js
import { createContext } from "react";
export default createContext({
video: "",
setVideo: () => {}
});
I added a timeout so you can see the loading part work as well. Let me know if there's something I'm missing about what you need to do.
I have tried some solutions that came by, on this link particularily...
I tried changing value inside my TodosContext.js file.. which didn't work, too..
One more thing that I have tried is to call useContext() function from another component, that didn't work neither..
Here's my code.
App.js:
import React, { useState, useContext } from 'react';
import TodoList from './components/TodoList';
import NewTodo from './components/NewTodo';
import { TodosProvider, TodosContext } from './components/contextapi/TodosContext';
function App() {
const [input, setInput] = useState('');
const [todos, setTodos] = useContext(TodosContext);
const _handleInput = (e) => {
setInput(e.target.value)
}
const _todoAdd = (e) => {
if (e.key === 'Enter') {
setTodos(
[...todos, { content: input, id: Date.now(), completed: false }]
)
setInput('')
}
}
const _todoRemove = (id) => {
const newTodos = todos.filter(todo => todo.id !== id)
setTodos(newTodos)
}
return (
<div>
<header>
<h3>To-Do Manager | Context API</h3>
</header>
<TodosProvider>
<NewTodo newTodo={_todoAdd} handleInput={_handleInput} newVal={input} />
<TodoList todos={todos} />
</TodosProvider>
</div>
);
}
export default App;
TodosContext.js:
import React, { useState, createContext } from 'react';
export const TodosContext = createContext()
export const TodosProvider = ({ children }) => {
const [todos, setTodos] = useState([]);
return (
<TodosContext.Provider value={[todos, setTodos]}>{children}</TodosContext.Provider>
)
}
TodoList.js:
import React, { useContext } from 'react';
import Todo from './Todo';
import RemoveTodoFromList from './RemoveTodoFromList';
import { TodosContext } from './contextapi/TodosContext'
function TodoList() {
const [todos, setTodos] = useContext(TodosContext);
return (
<div>
{todos.map(todo => (
<div>
<Todo key={todo.id} todo={todo} />
</div>
))}
</div>
)
}
export default TodoList
I'm really struggling with this one, I spent whole day figuring out what went wrong.. Thanks!
We fixed it inside the comments.
createContext needs an object as parameter that defines your context.
In your case it should be export const TodosContext = createContext([[],() => {}]).
So the application knows the first element of the tuple is an array and so iterable.