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]
};
Related
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>
My render page
import "./splash.css";
import { Redirect } from 'react-router-dom';
import React from 'react';
function Splash() {
const login = () => {
console.log('123')
return <Redirect to='/login' />;
}
const signup = () => {
return <Redirect to='/sign-up' />;
}
return (
<>
<div className="bannerContainer">
<h1>Costco Connect</h1>
</div>
<div className="formContainer">
<div
className="loginContainer"
onClick={login}
>
<h2>Login</h2>
</div>
<div
className="loginContainer"
onClick={signup}
>
<h2>Sign Up</h2>
</div>
</div>
</>
);
}
export default Splash;
And my app.js
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import SignUpForm from './components/auth/SignUpForm';
import NavBar from './components/NavBar';
import ProtectedRoute from './components/auth/ProtectedRoute';
import UsersList from './components/UsersList';
import User from './components/User';
import { authenticate } from './store/session';
import Tickets from './components/Tickets';
import Departments from './components/Departments';
import Splash from './components/Splash';
import LoginForm from './components/auth/LoginForm';
function App() {
const [loaded, setLoaded] = useState(false);
const dispatch = useDispatch();
useEffect(() => {
(async () => {
await dispatch(authenticate());
setLoaded(true);
})();
}, [dispatch]);
if (!loaded) {
return null;
}
return (
<BrowserRouter>
<ProtectedRoute >
<NavBar />
</ProtectedRoute>
<Switch>
<Route path='/login' exact={true}>
<LoginForm />
</Route>
<Route path='/sign-up' exact={true}>
<SignUpForm />
</Route>
<ProtectedRoute path='/users' exact={true} >
<UsersList />
</ProtectedRoute>
<ProtectedRoute path='/users/:userId' exact={true} >
<User />
</ProtectedRoute>
<Route path='/' exact={true} >
<Splash />
</Route>
<ProtectedRoute path='/departments' exact={true} >
<Departments />
</ProtectedRoute>
<ProtectedRoute path='/departments/:departmentId/tickets' exact={true} >
<Tickets />
</ProtectedRoute>
{/*
<ProtectedRoute path='/departments/:departmentId/tickets/:ticketId' exact={true} >
<Tickets />
</ProtectedRoute> */}
</Switch>
</BrowserRouter>
);
}
export default App;
Basically I'm trying to make it when you click on a div it will send you to the relevant form to either sign up or log in but I cannot figure out why the url isn't being redirected. If I throw a console.log inside of the functions they are running so I'm convinced the bug is in my app.js somewhere but I've run out of things to try. Thanks in advance, I've looked at other similar posts but no solutions have helped so far.
The solution was to use a NavLink instead, the Redirects were only working if they were inside a conditional and then returning.
I want to do a type of authorization in react, but I ran into a problem when I add a token in one component, the 2nd does not see it, but sees it only after reloading the application.
How can this be corrected in the react?
import React from 'react';
import {NavLink} from "react-router-dom";
const Login = () => {
const handleLogin = () => {
console.log( localStorage.getItem('token'));
localStorage.setItem('token', 'token');
}
return (
<div>
<header className="">
<ul>
<li><NavLink to="/main" activeClassName={'active-link'}>Home</NavLink></li>
</ul>
</header>
<button onClick={handleLogin}>login</button>
</div>
);
};
export default Login;
App.js
import './App.css';
import React from 'react';
import {Route,Redirect,Switch} from 'react-router-dom'
import Login from "./pages/login";
import Logout from "./pages/logout";
import Main from "./pages/main";
import ErrorPage from "./pages/error";
function App() {
return (
<div className="App">
<div>ore</div>
<Switch>
<Route exact path="/">
<Redirect to="/login" />
</Route>
{
localStorage.getItem('token') ? <Route path="/main" component={Main} /> : null
}
<Route path="/login" component={Login} />
<Route path="/portfolio" component={Logout} />
<Route path="/error" exact component={ErrorPage} />
<Redirect to={'/error'} />
</Switch>
</div>
);
}
export default App;
you can use the useEffect and useState into your app.js
const [token, setToken] = useState([]);
const tokenFromLogin = localStorage.getItem("token");
useEffect(() => {
setToken(tokenFromLogin);
}, [jwt]);
hi hope someone can help me. I have added pagination into my react app. I have checked and the query strings are working as they should be on the back end. However when I try and click through the pagination buttons it adds extra to the URL so instead of localhost:3000/topic/page/1 it is localhost:3000/topic/page/topic/page/1. Therefore I get a not found error when I click on the link.
Routes file
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Register from '../auth/Register';
import Login from '../auth/Login';
import Alert from '../layout/Alert';
import Dashboard from '../dashboard/Dashboard';
import CreateProfile from '../profile-forms/CreateProfile';
import EditProfile from '../profile-forms/EditProfile';
import Profiles from '../profiles/Profiles';
import Profile from '../profile/Profile';
import Posts from '../posts/Posts';
import Post from '../post/Post';
import Topics from '../topic/Topics';
import Flashcard from '../flashcard/Flashcard';
import Quiz from '../quiz/Quiz';
import Factsheet from '../factsheet/Factsheet';
import NotFound from '../layout/NotFound';
import PrivateRoute from '../routing/PrivateRoute';
const Routes = () => {
return (
<section className="container">
<Alert />
<Switch>
<Route exact path="/register" component={Register} />
<Route exact path="/login" component={Login} />
<PrivateRoute exact path="/profiles" component={Profiles} />
<PrivateRoute exact path="/profile/:id" component={Profile} />
<PrivateRoute exact path="/dashboard" component={Dashboard} />
<PrivateRoute exact path='/create-profile' component={CreateProfile} />
<PrivateRoute exact path='/edit-profile' component={EditProfile} />
<PrivateRoute exact path='/topic' component={Topics} />
<PrivateRoute exact path='/topic/search/:keyword' component={Topics} />
<PrivateRoute exact path='/topic/page/:pageNumber' component={Topics} />
<PrivateRoute exact path='/topic/search/:keyword/page/:pageNumber' component={Topics} />
<PrivateRoute exact path='/topic/flashcard/:id' component={Flashcard} />
<PrivateRoute exact path='/topic/quiz/:id' component={Quiz} />
<PrivateRoute exact path='/topic/factsheet/:id' component={Factsheet} />
<PrivateRoute exact path="/posts" component={Posts} />
<PrivateRoute exact path="/posts/:id" component={Post} />
<Route component={NotFound} />
</Switch>
</section>
);
};
export default Routes;
Paginate.js
import React from 'react'
import { Pagination } from 'react-bootstrap'
import { Link } from 'react-router-dom';
import { LinkContainer } from 'react-router-bootstrap'
const Paginate = ({ pages, page, keyword = '' }) => {
return (
pages > 1 && (
<Pagination>
{[...Array(pages).keys()].map((x) => (
<LinkContainer
tag={Link}
key={x + 1}
to={keyword
? `topic/search/${keyword}/page/${x + 1}`
: `topic/page/${x + 1}`
}
>
<Pagination.Item active={x + 1 === page}>{x + 1}</Pagination.Item>
</LinkContainer>
))}
</Pagination>
)
)
}
export default Paginate
SearchBox.js
import React, { useState } from 'react'
import { Form, Button } from 'react-bootstrap'
const SearchBox = ({ history }) => {
const [keyword, setKeyword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
if(keyword.trim()) {
history.push(`/topic/search/${keyword}`)
} else {
history.push('/topic')
}
}
return (
<Form onSubmit={submitHandler} inline>
<Form.Control
type="text"
name="q"
onChange={(e) => setKeyword(e.target.value)}
placeholder="Search Topics....."
></Form.Control>
</Form>
)
}
export default SearchBox
topic.js
import React, { Fragment, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Spinner from '../layout/Spinner';
import Paginate from '../layout/Paginate'
import TopicItem from './TopicItem';
import { getTopics } from '../../actions/topic';
const Topics = ({ getTopics, topic: { topics, loading, pages, page }, match }) => {
const keyword = match.params.keyword
const pageNumber = match.params.pageNumber || 1
useEffect(() => {
getTopics(keyword, pageNumber);
}, [getTopics, keyword, pageNumber])
return (
<Fragment>
{loading ? (
<Spinner />
) : (
<Fragment>
<h1 className="large text-primary1 my-2">Pick a Topic</h1>
<Link to="/topic" className="btn my-4">
Back To Topics
</Link>
<div className="card-grid-home">
{topics.length > 0 ? (
topics.map(topic => (
<TopicItem key={topic._id} topic={topic} />
))
) : (
<h4>No topics found......</h4>
)}
</div>
<Paginate pages={pages} page={page} keyword={keyword ? keyword : ''}/>
</Fragment>
)}
</Fragment>
)
};
Topics.prototype = {
getTopics: PropTypes.func.isRequired,
topic: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
topic: state.topic
})
export default connect(mapStateToProps, { getTopics })(Topics)
Thanks in advance
Realised that I was missing a / for the start of the routes. Silly mistake.
I am attempting to redirect from the root url of my app and I get the above error. I have read some of the other answers here on SO that reference this error but they center on state being updated cyclically and I can't see where I'm doing it in the Router:
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router, Route, Switch, Redirect} from 'react-router-dom'
import BookDetails from './BookDetails'
import NavBar from './NavBar'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<>
<Router>
<NavBar/>
<Switch>
<Redirect from="/" to="/search" component={App} /> {/** */}
<Route path="/search" component={App} />
<Route path="/" component={App} />
</Switch>
<Route path="/book/:bookId" component={BookDetails} />
</Router>
</>
, document.getElementById('root'));
Or in the App.js file which I hope is okay :
import React, {useState, useEffect} from 'react'
import {Link} from 'react-router-dom'
// import debounce from 'lodash'
import noimage from './noimage.png'
import axios from 'axios'
import './App.css';
const App = (props) =>{
const [books,setBooks] = useState([])
const [isLoading,setIsLoading] = useState(false)
const [hasError,setHasError] = useState(false)
const [searchTerm,setSearchTerm] = useState('')
console.log("props",(props.history.location.pathname))
const input = str => {
console.log(str)
setSearchTerm(str)
getData(str)
}
const getData = async () => {
if(searchTerm && searchTerm.length >= 2){
if(isLoading === false)setIsLoading(true)
let s = `http://localhost:7000/books/search/${searchTerm}`
await axios.get(s)
.then(resp => {
console.log(resp.data.books)
if(books === [])setBooks([...resp.data.books])
//props.history.push(s)
})
.catch(error => {
console.log(error)
setHasError(true)
setIsLoading(false)
})
}
if(isLoading === true) setIsLoading(false)
}
const img = {
height : "175px",
width : "175px"
}
// useEffect(() =>{
// setTimeout(() => getData(),2250)
// },[])
return isLoading ? (
<div className="App">
<div className="spinner"></div>
<div className="App loading">
<p><i>loading...</i></p>
</div>
</div>
)
: hasError ? (
<div className="App loading-error">
⚠ There is a network issue: Please try again later
</div>
)
:
(
<div className="App">
<div className="search">
<div className="container">
<div className="content">
<input
type="search"
placeholder="Search..."
aria-label="Search"
className="search-form"
value={searchTerm}
onChange={e => input(e.target.value)}
/>
</div>
</div>
</div>
{
(books && books.length >= 1) &&
books.map((b,i) => {
console.log(typeof b.imageLinks)
return (
<div key={`${b.title}-${i}`} className="search-book">
<Link to={`/book/${b.id}`}>
{
(b.imageLinks === undefined || b === undefined || b.imageLinks.thumbnail === undefined) ?
<img src={noimage} alt ="Missing" style={img}></img>
:
<img src={b.imageLinks.thumbnail} alt={b.title}></img>
}
</Link>
<p>{b.title}</p>
<hr/>
</div>
)}
)
}
</div>
)
}
export default App;
I can't demo the code since it involves a REST Api server that's local to my machine. Thanks.
Well my instructor suggested this : https://reacttraining.com/react-router/web/api/Switch
and I thought I tried this already but I read the docs and tried this -
(
<Router>
<NavBar/>
<Switch>
<Redirect exact from="/" to="/search" component={App} />
<Route path="/search" component={App} />
<Route path="/book/:bookId" component={BookDetails} />
</Switch>
</Router>
)
The exact property isn't needed on all the routes. But I thank Kishan for his effort, he definitely was on the right track. Thanks again.
<>
<Router>
<NavBar/>
<Switch>
<Redirect from="/" to="/search" component={App} /> {/** */}
<Route exact path="/search" component={App} />
<Route exact path="/book/:bookId" component={BookDetails} />
</Switch>
</Router>
</>
try this way, maybe helps you