React pass fetched data from API to another component - javascript

I am fetching few products from an API, and displaying them in card. There is a More Details link on the cards, where if the user clicks on it, it will take the user to the selected product details page. My routing to productDetails page works, But I am having troubles to find a way to pass the fetched data to the productDetails page as props.
This is what I have so far:
My FeaturedProduct.js:
import React from "react";
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import ProductDetails from "./ProductDetails";
import axios from "axios";
function FeaturedProduct(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts();
}, []);
function fetchProducts() {
axios
.get("https://shoppingapiacme.herokuapp.com/shopping")
.then((res) => {
console.log(res);
setProducts(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div>
<h1> Your Products List is shown below:</h1>
<div className="item-container">
{products.map((product) => (
<div className="card" key={product.id}>
{" "}
<h3>{product.item}</h3>
<p>
{product.city}, {product.state}
</p>
<Router>
<Link to="/productdetails">More Details</Link>
<Switch>
<Route path="/productdetails" component={ProductDetails} />
</Switch>
</Router>
</div>
))}
</div>
</div>
);
}
export default FeaturedProduct;
My Product Details Page:
import React from "react";
import FeaturedProduct from "./FeaturedProduct";
function ProductDetails(props) {
return (
<div>
<div>
<h1>{props.name}</h1>
<h1>{props.color}</h1>
</div>
</div>
);
}
export default ProductDetails;

I am still learning but this is what I would do:
<Route path="/productdetails">
<ProductDetails product={product}/>
</Route>
====
On ProductDetails you can destructure the props:
function ProductDetails(props) {
const {name, color} = props.product;
return (
<div>
<div>
<h1>{name}</h1>
<h1>{color}</h1>
</div>
</div>
);
}
export default ProductDetails;

Pass it as an element with props, if you are using v 6; sorry I didn't ask which version. >
<Switch>
<Route path="/productdetails" element={<ProductDetails {...props} />}/>
</Switch>
if version v4/5 use the render method >
<Route path="/productdetails" render={(props) => (
{ <ProductDetails {...props} />} )}/>

//pass it this way
<Switch>
<Route
path="/productdetails"
render={() => (
{ <ProductDetails product={product}/>})}/>
/>
</Switch>

Related

React.js - Functions are not valid as a React child

I am new to React.js. I can't solve the problem. I am getting this warning:
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
App.js
`
import React from 'react';
import MovieList from './MovieList';
import SearchBar from './SearchBar';
import AddMovie from './AddMovie';
import axios from 'axios'
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
class App extends React.Component {
state = {
movies: [],
searchQuery: ""
}
async componentDidMount() {
const response = await axios.get("http://localhost:3002/movies")
this.setState({movies: response.data})
}
deleteMovie = async (movie) => {
axios.delete(`http://localhost:3002/movies/${movie.id}`)
const newMovieList = this.state.movies.filter(
m => m.id !== movie.id
)
this.setState(state => ({
movies: newMovieList
}))
}
searchMovie = (event) => {
this.setState({searchQuery: event.target.value })
}
render() {
let filteredMovies = this.state.movies.filter(
(movie) => {
return movie.name.toLowerCase().indexOf(this.state.searchQuery.toLowerCase()) !== -1
}
)
return (
<Router>
<div className="container">
<Routes>
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
</Route>
<Route path='/add' element={<AddMovie />} />
</Routes>
</div>
</Router>
)
}
}
export default App;
`
What am I doing wrong?
Thanks in advance.
Passing a function to a route like you did:
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
looks like a router v5 syntax. This is not working in v6: you should pass an element, which is different than a function producing an element. Something like this would work:
<Route path='/' exact element={(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>

React Localstorage values resetting after refresh

when i add new items to favorites localStorage, all my items in favorites storage resetting and new added items saving in the localStorage. if i dont add any new item it works fine.
how can i stop the reset? im probably doing something wrong with setFavorite state but i don't know.
App.js
import "./App.css";
import React, { Profiler, useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Login from "./components/Login/Login";
import SignUp from "./components/SignUp/SignUp";
import Home from "./components/Home/Home";
import ChuckNorris from "./components/ListAPI/ChuckNorris/ChuckNorris";
import CreatePost from "./components/CreatePost/CreatePost";
import RandomUser from "./components/ListAPI/RandomUser/RandomUser";
import Pokemon from "./components/ListAPI/Pokemon/Pokemon";
import Valorant from "./components/ListAPI/Valorant/Valorant";
import Recipes from "./components/ListAPI/Recipes/Recipes";
import { MainContext } from "./components/Context";
import { getDocs, collection, deleteDoc, doc } from "firebase/firestore";
import { db, auth } from "./firebase";
import Favorites from "./components/Favorites/Favorites";
function App() {
const [isAuth, setIsAuth] = useState(localStorage.getItem("isAuth"));
const [modu, setModu] = useState(false);
const [postLists, setPostList] = useState([]);
const [favorites, setFavorites] = useState(localStorage.getItem("dam"));
const postsCollectionRef = collection(db, "posts");
useEffect(() => {
const getPosts = async () => {
const data = await getDocs(postsCollectionRef);
setPostList(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getPosts();
}, []);
useEffect(() => {
const localData = localStorage.getItem("dam") ?? [];
setFavorites(localData);
}, [setFavorites]);
const addFavorite = (favorite) => {
setFavorites([...favorites, favorite]);
localStorage.setItem("dam", JSON.stringify(favorite));
};
const data = {
postLists,
setPostList,
favorites,
setFavorites,
addFavorite,
};
return (
<>
<MainContext.Provider value={data}>
<Router>
<Routes>
<Route
exact
path="/"
element={<Home isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/login"
element={<Login isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/createpost"
element={<CreatePost isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/signup"
element={<SignUp isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/chucknorris"
element={<ChuckNorris isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/pokemon"
element={<Pokemon isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/randomuser"
element={<RandomUser isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/valorant"
element={<Valorant isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/recipes"
element={<Recipes isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
<Route
path="/favorites"
element={<Favorites isAuth={isAuth} setIsAuth={setIsAuth} />}
/>
</Routes>
</Router>
</MainContext.Provider>
</>
);
}
export default App;
Favorites.js
import React, { useEffect, useState } from "react";
import Sidebar from "../Sidebar/Sidebar";
import PostList from "../Home/PostList";
import { useContext, MainContext } from "../Context";
import "./Favorites.css";
const Favorites = ({ isAuth, setIsAuth, addFavorite }) => {
const { postLists, setPostList, favorites, setFavorites } =
useContext(MainContext);
return (
<>
{" "}
<div className="containers">
<div className="sidebar">
<Sidebar isAuth={isAuth} setIsAuth={setIsAuth} />
<div className="norris mt-4">
<div className="favorite-container">
<div className="new-container">
{postLists
.filter((post) => favorites.includes(post))
.map((post) => (
<PostList
post={post}
addFavorite={addFavorite}
key={post.id}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default Favorites;
Postlist.js
import React from "react";
const PostList = ({ post, addFavorite }) => {
const { linkin, title, imageURL, photoURL, name, id } = post;
return (
<>
<div>
<div className="post">
<div className="postimage">
<div className="del"></div>
<div className="images">
<a href={linkin}>
<p className="ss">{title}</p>
<img src={imageURL} id="img-photo" />
</a>
<div className="uploader">
<img src={photoURL} />
<p>by {name}</p>
</div>
{addFavorite && (
<div className="butons">
<button onClick={() => addFavorite(id)} id="favori">
+
</button>
</div>
)}
</div>
</div>
</div>
</div>
</>
);
};
export default PostList;
Your addFavorite function saves favorite to local storage instead of saving it the same way you set your local state [...favorites, favorite]
const addFavorite = (favorite) => {
setFavorites([...favorites, favorite]);
localStorage.setItem("dam", JSON.stringify(favorite)); <- this also needs to be [...favorites, favorite]
};

How to pass data from a child to another a child (nested in Home page) in React?

I'm struggling to figure out how to pass the search term from ChildOne to ChildTwo (which is nested in a page). I hope all the code I provided down below will make it clear. I tried to lift up the state to the App.js component but it didn't work or maybe I didn't do it correctly. I would appreciate any help. Thanks in advance :)
Child 1:
const ChildOne = () => {
const [searhTerm, setSearchTerm] = useState("");
return(
<InputContainer>
<input
type="text"
placeholder="Find a recipe"
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
}}
/>
<SearchIcon />
</InputContainer>
)
}
Child 2:
const ChildTwo = () => {
// I want to pass the searchTerm to be used in a fetch request in this component
const apiURL = `'url' + {searchTerm}`;
return(
...
)
}
App.js
function App(){
return(
<>
<ChildOne/>
<Switch>
<Route path="/" exact component={Home}/>
<Switch/>
</>
)
}
Home.js:
const Home = () => {
return (
<>
<ChildTwo />
</>
);
};
there is several way to do that...
I suggest you use Context Api.
if you don't want to use Context Api or State management
see this example
enter link description here
import { useState } from "react";
import {
Route,
Switch,
BrowserRouter as Router,
RouterProps
} from "react-router-dom";
import ChildOne from "./ChildOne";
import Home from "./Home";
function App() {
const [value, setValue] = useState("");
return (
<>
<ChildOne setValue={setValue} />
<Router>
<Switch>
<Route path="/" exact>
<Home value={value} />
</Route>
</Switch>
</Router>
</>
);
}
export default App;

React Hook Component using old value of state which is passed through useContext

Please see this sandbox:
https://codesandbox.io/s/use-context-simple-qygdz?file=/src/App.js
*** You have to go to /check1 to start, and when you reach /check2 there shouldn't be a ddd, but it's still there right now (state not updated)
When I've linked one page to another, the usecontext does not pass the state. Not sure why - but I am glad that with help we were able to pinpoint exactly where the problem is.
maybe it helps if you just use one useState hook from which you update your entire context I included the main parts below (here is a link to a working sample). When i try this i see context changes in every component.
import React from "react";
import "./styles.css";
import ChangeContext from "./components/ChangeContext";
import ViewChange from "./components/ViewChange";
const info = {
artists: null,
messages: null,
songs: null,
userid: "ddd",
accesstoken: null,
refreshtoken: null
};
export const InfoContext = React.createContext();
export default function App() {
const [context, setContext] = React.useState(info);
return (
<InfoContext.Provider value={[context, setContext]}>
<div className="App">
<ChangeContext />
<ViewChange />
</div>
</InfoContext.Provider>
);
}
and then in a component
import React from "react";
import { InfoContext } from "../App";
export default function App() {
const [context, setContext] = React.useContext(InfoContext);
return (
<div className="App">
<h1>{context.userid} uid</h1>
<button
onClick={e => {
setContext({ ...context, userid: 123 });
}}
>
click me
</button>
</div>
);
}
in another component check for changes
import React from "react";
import { InfoContext } from "../App";
export default function ChangeContext() {
const [context, setContext] = React.useContext(InfoContext);
return (
<div className="App">
<h1>{context.userid} uid</h1>
<button
onClick={e => {
setContext({ ...context, userid: 123 });
}}
>
click me
</button>
</div>
);
}
maybe try this instead
const [context, setContext] = useState(info);
return (
<BrowserRouter>
<Route exact path="/signup/:id/:access_token" render={() => <InfoContext.Provider value={[context, setContext]}><Signup /> </InfoContext.Provider>} />
<Route exact path="/" render={() => <Login />} />
<Route exact path="/home/:id/:access_token/:refresh_token" render={() => <Homepage ></Homepage>} />
<Route exact path="/artist/:artistid" render={() => <ArtistPage ></ArtistPage>} />
<Route exact path="/map" render={() => <MapLeaflet />} />
</BrowserRouter>
);
I can't comment yet, but is the userId being updated in the context?
What is the value for console.log(userid) inside artisthomepage.js? Maybe it renders with the old value but then it receives the new one and doesn't re-render the component.

React Redux cannot pass props of a functional component

What im trying to achieve in here is to being able to click on a image and render that clicked movie’s info. The problem is the i can not find a way to match id of the clicked movie and the detailed movie. As a result the singleMovierequest has undefined id which causes 404 error. Here is codesandbox link: https://codesandbox.io/s/modern-http-coy0w (Api key is typed as '???' intentionally). Here is movie and app components.
const Movie = (props) => {
const movie = props.singleMovie
const fetchMovie = props.initializeSingleMovie
useEffect(() => { fetchMovie(props.id) }, [props.id])
return (
<div>
<h2>{movie.title}</h2>
<p>{movie.overview}</p>
</div>
)
}
render part of the app component:
<Container>
<h2>Movieapp</h2>
<Router>
<Menu />
<Route exact path="/popular" render={() =>
<PopularMovies />
} />
<Route exact path="/search" render={() =>
<Movies />
} />
<Route exact path="/search/:id" render={(props) => <Movie key={props.match.params.id} />} />
} />
<Route exact path="/popular/:id" render={(props) => <Movie key={props.match.params.id} />} />
</Router>
</Container>
"initializeSingleMovie" is an action,You named it reducer but its an action,for the sake of solving this problem ,you have to use mapDisptachToProps and dispatch(it will access the store methods),below is a modifed Movie.js File.In future have a separate action folder for api hits.Compartmentalise more,hope it helps.
import React from 'react'
import { connect } from 'react-redux'
import { useEffect } from 'react'
import { initializeSingleMovie } from '../reducers/singleMovieReducer'
const Movie = (props) => {
console.log(props,"");
const movie = props.singleMovie
props.initializeSingleMovie(props.id)
return (
<div>
<h2>{movie.title}</h2>
<p>{movie.overview}</p>
</div>
)
}
const mapStateToProps = (state) => {
return {
singleMovie: state.singleMovie
}
}
const mapDispatchToProps = dispatch => {
return {
initializeSingleMovie: (id) => dispatch(initializeSingleMovie(id)),
};
};
export default connect(
mapStateToProps,
mapDisptachToProps
)(Movie)

Categories