React : How to make my SearchBar render in another Page? - javascript

I have 2 pages both are childrens from App.
_ NavbarComp.js
_ Home.js
Right now I have a functionnal SearchBar in my Home, I'd like to pass the values between NavbarComp & Home.
When I search from my NavbarComp, I'd like to have a render only on the Home page.
This is what it looks like so far.
I now need to remove my input button from Home, and pass the values between both pages Navbar & Home.
I dont think I can use props there, tho.. looking for solutions !
NavbarComp.js
import { Layout } from "antd";
import { Link, useHistory } from "react-router-dom";
import Cookies from "js-cookie";
import { useDispatch } from "react-redux";
import { logout } from "redux/auth/authActions";
import { Nav, Navbar, NavDropdown, Form, FormControl, Button, Row, Col, Container } from "react-bootstrap";
const { Header } = Layout;
export const NavbarComp = ({input, setInput}) => {
const history = useHistory();
const cookie = Cookies.get('token');
const dispatch = useDispatch();
const logMeOut = (e) => {
e.preventDefault();
dispatch(logout());
history.push('/');
}
return (
<>
<Navbar bg="light" expand="lg">
<Form className="d-flex">
<FormControl
type="search"
placeholder="Search"
className="mr-2"
aria-label="Search"
input={input}
/>
</Navar>
</>
)
}
Home.js
import React from 'react';
import { Link } from "react-router-dom";
import { useSelector } from 'react-redux';
import { v4 as uuid_v4 } from "uuid";
export const Home = () => {
const connected = useSelector(state => state.auth.user)
const [input, setInput] = React.useState('');
const [flats, setFlats] = React.useState([]);
const [flatsDefault, setFlatsDefault] = React.useState([]);
React.useEffect(() => {
getListing('real_estates')
}, [])
const getListing = async (url) => {
const config = {
method: 'GET',
};
const response = await fetch(`${process.env.REACT_APP_API_URL}/${url}`, config);
const data = await response.json();
setFlats(data)
setFlatsDefault(data)
};
const updateInput = async (input) => {
const filtered = flatsDefault.filter(flat => {
return flat.title.toLowerCase().includes(input.toLowerCase())
})
setInput(input);
setFlats(filtered);
}
return (
<>
<input
type='text'
input={input}
placeholder={"Search for properties"}
onChange={(e) => updateInput(e.target.value)}
/>
<div className="home-header">
<div className="bg-img-desc">
<h1>List of real estates</h1>
</div>
</div>
<div className="container" style={{ padding: '0 3.5rem' }}>
<ul className="row">
{flats ? (
flats.map(flat => (
<li className="col-12 col-sm-6 col-md-4 col-lg-3 mb-4" key={uuid_v4()}>
<div className="card h-100">
{ flat.images_url && <img src={`${process.env.REACT_APP_API_URL}${flat.images_url[0]}`} className="card-img-top" alt=""/> }
<div className="card-body">
<h5>{flat.title}</h5>
<p>Price : {flat.price} €</p>
<p>location : {flat.location}</p>
{connected && <Link to={`/real_estates/${flat.id}`} className="btn btn-primary">Details</Link>}
</div>
</div>
</li>
))
) : (
<h2>Loading...</h2>
)}
</ul>
</div>
</>)
}
App.js
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import { Layout } from 'antd';
import { NavbarComp } from "components/NavbarComp";
import { Home } from "pages/Home";
import Realestate from "pages/Realestate";
import NewRealestate from "pages/NewRealestate";
import { Other } from "pages/Other";
import { Login } from "pages/Login";
import { Register } from "pages/Register";
import Profile from "pages/Profile";
import { useDispatch, useSelector } from 'react-redux';
import { getUser } from 'redux/auth/authActions';
import Cookies from 'js-cookie';
function App() {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
React.useEffect(() => {
dispatch(getUser(Cookies.get('id')))
console.log(user)
}, [])
return (
<Layout className="layout" style={{ backgroundColor: 'transparent' }}>
<Router>
<NavbarComp />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/real_estates/:id">
<Realestate />
</Route>
<Route path="/new_real_estate">
<NewRealestate />
</Route>
<Route path="/other">
<Other />
</Route>
<Route exact path="/login" >
<Login />
</Route>
<Route exact path="/register" >
<Register />
</Route>
<Route exact path="/profile" >
<Profile user={user} />
</Route>
</Switch>
</Router>
</Layout>
);
}
export default App;

I can see three ways that you can implement to resolve this.
The first one is creating a context, so your state is going to be share through all your app. So when you change it inside your NavbarComp you will be able to get this value in another component.
The second one is similar to context but using redux. I saw that you are using redux in your project, you can share those values through redux.
The other way is to create a useState() inside your app component and pass it as props to your NavbarComp and your home. So this state will be shared between those two.
function App() {
const dispatch = useDispatch();
const user = useSelector(state => state.auth.user);
const [value, setValue] = useState()
React.useEffect(() => {
dispatch(getUser(Cookies.get('id')))
console.log(user)
}, [])
return (
<Layout className="layout" style={{ backgroundColor: 'transparent' }}>
<Router>
<NavbarComp value={value} setValue={setValue}/>
<Switch>
<Route exact path="/">
<Home value={value} setValue={setValue}/>
</Route>
<Route exact path="/real_estates/:id">
<Realestate />
</Route>
<Route path="/new_real_estate">
<NewRealestate />
</Route>
<Route path="/other">
<Other />
</Route>
<Route exact path="/login" >
<Login />
</Route>
<Route exact path="/register" >
<Register />
</Route>
<Route exact path="/profile" >
<Profile user={user} />
</Route>
</Switch>
</Router>
</Layout>
);
}
export default App;

Related

Why React does not render component when I redirect to product page

I have an error and can not find any solution in google. The error appears when I want to go to a poduct page and press a button on home page to go to a product page and there I don't have any element rendered, I used React Route to user be able to go to product page and add it to a cart and suppose I did something wrong with providing path but not sure.
VM867:236 Matched leaf route at location "/2" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.
Here is a code for Item Element:
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
function Item() {
const { id } = useParams();
const [item, setItem] = useState([]);
const [loading, setLoading] = useState(false);
console.log("item", item);
// Fetching Data
useEffect(() => {
const fetchedData = async () => {
setLoading(true);
const response = await fetch(`https://fakestoreapi.com/products/${id}`);
const data = response.json();
setItem(data);
};
fetchedData();
}, []);
return (
<div className="container">
{loading ? (
<>
<h3>Loading.....</h3>
</>
) : (
<div className="container">
<p>{item.title}</p>
</div>
)}
</div>
);
}
export default Item;
And for App.js
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import ItemsComponent from "./Componets/ItemsComponent";
import Navbar from "./Componets/Navbar";
import Home from "./Componets/Home";
import Item from "./Componets/Item";
import About from "./Componets/About";
import AboutLink from "./Componets/AboutLink";
import Contact from "./Componets/Contact";
import Footer from "./Componets/Footer";
import "./App.css";
function App() {
return (
<>
<Router>
<Navbar />
<Routes>
<Route
exact
path="/"
element={
<>
<Home />
<ItemsComponent />
</>
}
/>
<Route exact path="/:id" component={<Item />} />
<Route exact path="/about" element={<About />} />
</Routes>
<AboutLink />
<Footer />
</Router>
</>
);
}
export default App;
It also affected all styling.
Maybe it's because you have
<Route exact path="/:id" component={<Item />} />
instead of
<Route exact path="/:id" element={<Item />} />
?
I think that instead of this
<Route exact path="/:id" component={<Item />} />
you want either of those
<Route exact path="/:id" component={Item} />
<Route exact path="/:id" element={<Item />} />

react router render component multiple times when refreshing

I'm trying to understand why when refreshing the page, the component is called multiple times:
MainLayout.tsx: (routes component)
import { FC, ReactElement, useEffect } from 'react'
import { Routes, Route, BrowserRouter } from 'react-router-dom'
import { createBrowserHistory } from 'history'
import { useLocation } from 'react-router-dom'
import { Navigate } from 'react-router-dom'
import { Outlet } from 'react-router-dom'
import { IntroductionPage } from '../pages/introduction/introduction-page'
import { useTranslation } from 'react-i18next'
import { storage } from '../utils/storage'
export const history = createBrowserHistory()
export const MainLayout: FC = () => {
const { t } = useTranslation()
useEffect(() => {
console.log('MainLayout:: constructor')
}, [])
const RequireAuth = (): ReactElement => {
const { token } = storage.getState().authReducer
let location = useLocation()
if (!token) return <Navigate to="/login" state={{ from: location }} />
return <Outlet />
}
return (
<BrowserRouter history={history}>
<div className="main-wrapper">
<div className="content-wrapper">
<div className="main-content">
<Routes>
<Route path="/" element={<Login />} />
<Route path="/login" element={<Login />} />
<Route element={<RequireAuth />}>
<Route path="/acceptance" element={<AcceptancePage />} />
<Route path="/introduction/:page" element={<IntroductionPage />} />
</Route>
</Routes>
</div>
</div>
</div>
</BrowserRouter>
)
}
introduction page
I put the following code in introduction page:
useEffect(() => {
console.log('IntroductionPage:: constructor')
setIntroduction(introductions[+page - 1])
}, [])
I'm refreshing the introduction page, and see in the console:
IntroductionPage:: constructor
MainLayout.tsx:23 MainLayout:: constructor
IntroductionPage:: constructor
IntroductionPage:: constructor
Appreciate any help
Ah, I see why the IntroductionPage component is mounted twice. The RequireAuth component is declared inside another React component. Since it is redeclared each render cycle it's a new React component reference so React unmounts the instance from the previous render cycle and mounts a new instance. All children it renders will also be new instances.
It should be declared out on its own, outside of any other React component.
Example:
const RequireAuth = (): ReactElement => {
const { token } = storage.getState().authReducer;
const location = useLocation();
if (!token) return <Navigate to="/login" state={{ from: location }} />;
return <Outlet />;
};
export const MainLayout: FC = () => {
const { t } = useTranslation();
useEffect(() => {
console.log('MainLayout:: constructor');
}, []);
return (
<BrowserRouter history={history}>
<div className="main-wrapper">
<div className="content-wrapper">
<div className="main-content">
<Routes>
<Route path="/" element={<Login />} />
<Route path="/login" element={<Login />} />
<Route element={<RequireAuth />}>
<Route path="/acceptance" element={<AcceptancePage />} />
<Route path="/introduction/:page" element={<IntroductionPage />} />
</Route>
</Routes>
</div>
</div>
</div>
</BrowserRouter>
);
};

In how many ways we can pass props to all child components in react

I have an app code
import React from "react";
import { Route, Switch } from "react-router-dom";
import Minidrawer from './components/Drawer/Minidrawer'
import { makeStyles } from '#mui/styles';
import Box from '#mui/material/Box';
import Main from "./components/Main/Main";
import {useSelector} from 'react-redux'
const useStyles = makeStyles({
container: {
display: "flex"
}
});
export default function App() {
const classes = useStyles();
const user = useSelector((state) => state.auth);
return (
<Box sx={{ display: 'flex' }}>
<Minidrawer currUser={user}/>
<Switch>
<Route exact from="/" render={props => <Main childText="home" currUser={user} {...props} />} />
<Route exact path="/auth" render={props => <Main childText="auth" currUser={user} {...props} />} />
<Route exact path="/register-client" render={props => <Main childText="registerClient" currUser={user} {...props} />} />
</Switch>
</Box>
);
}
I have to pass currUser to all child components imported in App but I do not want to duplicate the code, what are different ways to achieve this so that all of the components have access to currUser?
if I understand what you want to do, you want to pass props to all children of a component, if the components are simple components you can do as follows:
import React from "react";
import Main from "./Main";
import PassPropsToNormalComponents from "./PassPropsToNormalComponents";
export default function App() {
const user = {
username: "lakhdar"
};
return (
<div style={{ display: "flex" }}>
<PassPropsToNormalComponents currUser={user}>
<Main childText="home" />
<Main childText="auth" />
<Main childText="registerClient" />
</PassPropsToNormalComponents>
</div>
);
and this is the PassPropsToNormalComponents file
import React from "react";
export default function PassPropsToNormalComponents({ children, ...props }) {
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, { ...child.props, ...props });
}
return child;
});
return <>{childrenWithProps}</>;
}
but in your case passing the props to the routes wont' make the routes pass the props to their rendered components so we need an extra step here:
first the file where we provide the props to the parent:
import React from "react";
import { Route, Switch } from "react-router-dom";
import Main from "./Main";
import PassPropsToRouteComponents from "./PassPropsToRouteComponents";
export default function App() {
const user = {
username: "lakhdar"
};
return (
<div style={{ display: "flex" }}>
<Switch>
<PassPropsToRouteComponents currUser={user}>
<Route
exact
from="/"
render={(props) => {
return <Main childText="home" {...props} />;
}}
/>
<Route
exact
path="/auth"
render={(props) => <Main childText="auth" {...props} />}
/>
<Route
exact
path="/register-client"
render={(props) => <Main childText="registerClient" {...props} />}
/>
</PassPropsToRouteComponents>
</Switch>
</div>
);
}
and finally, the extra step is to get the rendered element and pass it its own props + the props from the parent, and the file looks like this:
import React from "react";
export default function PassPropsToRouteComponents({ children, ...props }) {
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
const routerChild = child.props.render();
return React.cloneElement(child, {
...child.props,
render: () => {
return React.cloneElement(routerChild, {
...routerChild.props,
...props
});
}
});
}
return child;
});
return <>{childrenWithProps}</>;
}
link to working codesandbox: https://codesandbox.io/s/gracious-meadow-dj53s
I hope this is what you've been looking for.
You could use redux or the context API.
Redux: https://react-redux.js.org/
Context API: https://reactjs.org/docs/context.html

React-router Route not rendering component

I'm trying to use react router in the following code, and render the RecipeCardDetail component. URL changes but nothing renders. It just renders when I click URL and press enter. I'm trying to show recipeId (URL Parameter) in the RecipeCardDetail component. I would like some help if possible.
Below my components.
App.js
import React, { useEffect, useState } from 'react';
import './App.css';
import RecipeCard from './components/RecipeCard/RecipeCard';
import RecipeCardDetail from "./components/RecipeCardDetail/RecipeCardDetail"
import Form from './components/Form/Form';
import axios from 'axios';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
function App() {
const [recipes, setRecipes] = useState([])
const [filter, setFilter] = useState("")
const [query, setQuery] = useState("")
useEffect(() => {
getRecipe()
}, [query])
const getRecipe = () => {
const key = process.env.REACT_APP_RECIPE_SEARCH_APP_API_KEY
const id = process.env.REACT_APP_RECIPE_SEARCH_APP_ID
axios.get(`https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${id}&app_key=${key}`)
.then(response => {
console.log(response.data.hits)
setRecipes(response.data.hits)
})
.catch(error => {
console.log(error)
})
}
return (
<Router>
<div className="App">
<div className="container">
<Form setFilter={setFilter} setQuery={setQuery} filter={filter} />
<div className="row">
<Switch>
<Route exact path="/" >
{recipes.map((item, id) => {
return <RecipeCard key={id} img={item.recipe.image} title={item.recipe.label} ingredients={item.recipe.ingredientLines} recipeId={id} />
})}
</Route>
<Route path="/recipe-card-detail/:recipeId" >
<RecipeCardDetail />
</Route>
</Switch>
</div>
</div>
</div>
</Router>
);
}
export default App;
RecipeCard.js
import React from 'react'
import "./style.css"
import "../../../node_modules/bootstrap/dist/css/bootstrap.min.css"
import { BrowserRouter as Router, Link } from "react-router-dom"
function RecipeCard( { img, title, ingredients, recipeId } ) {
return (
<Router>
<div className="col-xl-4 col-md-6 col-s-12">
<div className="card mt-5" style={{ width: "24rem" }}>
<img src={img} className="card-img-top" alt="img" />
<div className="card-body">
<h3 className="text align">{title}</h3>
<ul className="card-text">
{ingredients.map((item, id) => {
return <li key={id} >{item}</li>
})}
</ul>
<Link to={`/recipe-card-detail/${recipeId}`}>Details</Link>
</div>
</div>
</div>
</Router>
)
}
export default RecipeCard
Try changing the order in which you are defining the routes.
<Switch>
<Route path="/recipe-card-detail/:recipeId" >
<RecipeCardDetail />
</Route>
<Route exact path="/" >
{recipes.map((item, id) => {
return <RecipeCard key={id} img={item.recipe.image} title={item.recipe.label} ingredients={item.recipe.ingredientLines} recipeId={id} />
})}
</Route>
</Switch>

React router renders blank page using exact on all routes

I have a problem with one of my components in which react router returns a blank page when accessing it. It only happens in one component and I do not know why.The component that has the problem is the EditPost component.
App.js
This is where all the routes are.
import "./App.css";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./components/Home.js";
import Signup from "./components/signup/Signup";
import Login from "./components/Login/Login";
import Dashboard from "./components/Dashboard/Dashboard";
import PrivateRoute from "./components/common/PrivateRoutes";
import { Provider } from "react-redux";
import Store from "./Store";
import { Provider as AlertProvider } from "react-alert";
import AlertTemplate from "react-alert-template-basic";
import { loadUser } from "./actions/auth";
import { useEffect } from "react";
import PostPage from "./components/PostPage/PostPage";
import EditPost from "./components/EditPost/EditPost";
// Alert options
const alertOptions = {
timeout: 3000,
};
function App() {
useEffect(() => {
Store.dispatch(loadUser());
}, []);
return (
<div className="App">
<Provider store={Store}>
<AlertProvider template={AlertTemplate} {...alertOptions}>
<Router>
<Switch>
<Route exact path="/post-edit/:id" component={EditPost} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/login" component={Login} />
<PrivateRoute exact path="/dashboard" component={Dashboard} />
<Route exact path="/" component={Home} />
<Route exact path="/post/:id" component={PostPage} />
</Switch>
</Router>
</AlertProvider>
</Provider>
</div>
);
}
export default App;
EditPost.js
This is the component that does not render
import React, { useState } from "react";
import { Form, Button } from "react-bootstrap";
import { connect } from "react-redux";
import { updatePost } from "../../actions/posts";
const EditPost = (props) => {
console.log(props);
const [title, setTitle] = useState(props.location.postData.title);
const [description, setDescription] = useState(
props.location.postData.description
);
const [body, setBody] = useState(props.location.postData.body);
const titleChange = (e) => {
setTitle(e.target.value);
};
const bodyChange = (e) => {
setBody(e.target.value);
};
const desChange = (e) => {
setDescription(e.target.value);
};
const addClick = (e) => {
e.preventDefault();
const post = { title, body, description };
props.updatePost(post);
};
return (
<div className="mt-4">
<h1>Edit Post</h1>
<Form>
<Form.Group>
<Form.Label>Post Title</Form.Label>
<Form.Control onChange={titleChange} type="text" />
</Form.Group>
<Form.Group>
<Form.Label>Short Description</Form.Label>
<Form.Control onChange={desChange} type="text" />
</Form.Group>
<Form.Group>
<Form.Label>Body</Form.Label>
<Form.Control onChange={bodyChange} as="textarea" rows={3} />
</Form.Group>
<Button onClick={addClick} variant="primary" type="submit">
Edit
</Button>
</Form>
</div>
);
};
const mapDispatchToProps = (dispatch) => {
return {
updatePost: (id, post) => dispatch(updatePost(id, post)),
};
};
export default connect(null, mapDispatchToProps)(EditPost);
Post.js
In this component is the link to the EditPost component.
import React from "react";
import { Card } from "react-bootstrap";
import dateFormat from "dateformat";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
const Post = (props) => {
return (
<Card>
<Card.Body>
<Link to={`/post/${props.id}`}>
<Card.Title>{props.title}</Card.Title>
</Link>
<Card.Text>{props.description}</Card.Text>
</Card.Body>
<Card.Footer>
<div className="d-flex justify-content-between">
<small className="text-muted">
{dateFormat(props.created, "mmmm dS, yyyy")}
</small>
{props.postUser === props.user ? (
<Link to={`/edit-post/${props.id}`}>
<i className="fas fa-edit" style={{ color: "grey" }}></i>
</Link>
) : null}
</div>
</Card.Footer>
</Card>
);
};
const mapStateToProps = (state) => {
if (state.auth.isAuthenticated) {
return {
user: state.auth.user.username,
};
} else {
return {};
}
};
export default connect(mapStateToProps)(Post);
Right now I am just trying to get the component rendered, then I will worry about the props. Thanks in advance.
Your link is /edit-post/${props.id}.
Your matched path is /post-edit/:id.
edit-post and post-edit are not the same thing 😉
I would recommend adding a NoMatch block at the end of your Switch. It'll help you to diagnose these problems faster.
<Route>
<NoMatch />
</Route>

Categories

Resources