I'm new to React so I've been following a lot of tutorials to understand certain functionality. Currently I've implemented bottom navigation but I can't find a solution on only displaying bottom navigation after I log in.
Here's my app.js code:
import { Container } from 'react-bootstrap';
import Login from './components/Login';
import Register from './components/Register';
import SearchPage from './components/SearchPage';
import Profile from './components/Profile';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import React, { useState } from "react";
import MuiBottomNav from './components/MuiBottomNav';
function App() {
return (
<>
<Container className='d-flex align-items-items-center'
style={{ minHeight: "100vh" }}>
<div className='w-100' style={{ maxWidth: "400px" }}>
<Router>
<AuthProvider>
<MuiBottomNav />
<Routes>
<Route exact path="/" element={<SearchPage />} />
<Route path='/register' element={<Register />} />
<Route path='/login' element={<Login />} />
<Route path='/profile' element={<Profile />} />
</Routes>
</AuthProvider>
</Router>
</div>
</Container>
</>
)
}
export default App;
Here's the bottom nav component
import AddHomeIcon from '#mui/icons-material/AddHome'
import SearchIcon from '#mui/icons-material/Search';
import { BottomNavigation, BottomNavigationAction } from "#mui/material";
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom';
const MuiBottomNav = () => {
const [bnValue, setBnValue] = useState(0);
const navigate = useNavigate();
return (
<div>
<BottomNavigation
sx={{ width: "100%", position: "absolute", bottom: 0 }}
value={bnValue}
onChange={(event, value) => setBnValue(value)}
>
<BottomNavigationAction
label="Profile"
value={bnValue}
onClick={() => navigate("/profile")}
icon={<AddHomeIcon />}
/>
<BottomNavigationAction
label="Search"
value={bnValue}
onClick={() => navigate("/")}
icon={<SearchIcon />}
/>
</BottomNavigation>
</div>
)
}
export default MuiBottomNav
At the moment the bottom nav is displayed even on the Register and Login page. I'd like to only see it after logging in.
import React, { useContext, useEffect, useState } from "react";
import { auth } from '../firebase'
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function logout() {
return auth.signOut()
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
signup,
login,
logout
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
The MuiBottomNav can access the AuthContext and conditionally render the nav contents if there is a truthy currentUser.
const MuiBottomNav = () => {
const { currentUser } = useAuth(); // <-- access context
const [bnValue, setBnValue] = useState(0);
const navigate = useNavigate();
if (!currentUser) return null; // <-- no user, return null
return (
<div>
<BottomNavigation
sx={{ width: "100%", position: "absolute", bottom: 0 }}
value={bnValue}
onChange={(event, value) => setBnValue(value)}
>
<BottomNavigationAction
label="Profile"
value={bnValue}
onClick={() => navigate("/profile")}
icon={<AddHomeIcon />}
/>
<BottomNavigationAction
label="Search"
value={bnValue}
onClick={() => navigate("/")}
icon={<SearchIcon />}
/>
</BottomNavigation>
</div>
);
}
Related
So I am trying to search an api and return search results, and it works, but I have to press enter twice to get the results to show up. And when I clear the search bar I have to press enter twice for all of the api data to be returned.
My goal is to have the search results live update on the /beaches page as you type, and if you click the beaches nav-link it returns all of the api data until you start to type your query. I am not sure what I'm doing wrong. Here is my code.
import './App.scss';
import { Routes, Route, useNavigate } from 'react-router-dom'
import { Homepage } from './components/Homepage/Homepage';
import { About } from './components/About/About';
import Navbar from './components/Navbar/Navbar';
import Beaches from './components/Beaches/Beaches';
import { useState } from 'react';
function App() {
const [searchResults, setSearchResults] = useState([]);
return (
<div className="App">
<Navbar setSearchResults={setSearchResults}/>
{/* below is routing example */}
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/about" element={<About />} />
<Route path="/beaches" element={<Beaches searchResults={searchResults} />} />
</Routes>
</div>
);
}
export default App;
Navbar
import { Link, useNavigate } from 'react-router-dom'
import { Container, Nav, Form} from 'react-bootstrap';
import { useState, useEffect } from 'react';
import axios from 'axios';
export default function Navbar({setSearchResults}) {
const [searchQuery, setSearchQuery] = useState('');
const [filteredResults, setFilteredResults] = useState([]);
const navigate = useNavigate();
const handleSearchInputChange = (event) => {
setSearchQuery(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
const response = await axios.get(`api/v1/beaches`);
setFilteredResults(
response.data.filter(result =>
result.name.toLowerCase().includes(searchQuery.toLowerCase())
)
);
setSearchResults(filteredResults);
navigate("/beaches");
};
return (
<div className="navbar">
<Container fluid>
<Link className="nav-link" to="/"><h2>🏖️</h2></Link>
{/* <Navbar.Toggle aria-controls="navbarScroll" /> */}
{/* <Navbar.Collapse id="navbarScroll" className="justify-content-end"> */}
<Nav
className="my-2 my-lg-0"
style={{ maxHeight: '100px' }}
navbarScroll
>
<Form className="d-flex mx-4" onSubmit={handleSubmit}>
<Form.Control
icon="search"
type="search"
placeholder="Search a location!"
className="me-2"
aria-label="Search"
value={searchQuery}
onChange={handleSearchInputChange}
/>
</Form>
<Nav.Link as={Link} to="/beaches" className="mx-4">Beaches</Nav.Link>
<Nav.Link as={Link} to="/about" className="mx-4">About</Nav.Link>
<Nav.Link as={Link} to="/contribute" className="mx-4">Contribute</Nav.Link>
</Nav>
{/* </Navbar.Collapse> */}
</Container>
</div>
)
};
Beaches
import React, {useState, useEffect} from 'react';
import BeachCard from '../BeachCard'
import axios from 'axios';
const Beaches = ({ searchResults }) => {
const [apiData, setApiData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await axios.get(`api/v1/beaches`);
setApiData(response.data);
};
fetchData();
}, []);
useEffect(() => {
if (searchResults.length > 0) {
setApiData(searchResults);
}
}, [searchResults]);
return (
<div className="beaches">
<h2>Results</h2>
{apiData.length > 0 ?(
<ul>
{apiData.map((result) => (
<BeachCard
key={result.id}
name={result.name}
description={result.description}>
</BeachCard>
))}
</ul>
) : (
<div>No Results Found</div>
)}
</div>
);
};
I'm fairly new to react so any help would be appreciated!!
I'm trying to make a private route from my login and signup pages to my dashboard page, but all the tutorials and guides that I've stumbled upon all require some sorta AuthContext thing and I didn't implement my authentication procedure using AuthContext.
I've tried different ways but none of them work and just end up giving me a blank page when I get to the dashboard page, what can I do to make it a private route? Using Firebase v9 btw.
SignUp.js
import './Signup.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { auth }from '../../../firebase';
import { createUserWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth';
import { Typography, Container, TextField, Button, Alert } from '#mui/material';
const Signup = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState('');
const [user, setUser] = useState({});
const history = useHistory();
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
})
const signup = async (e) => {
e.preventDefault();
if (password !== confirmPassword) {
return setError("Passwords do not match")
}
try {
const user = await createUserWithEmailAndPassword(
auth,
email,
password
);
history.push("/dashboard/canvas");
} catch (err) {
setError(err.message);
}
}
return (
<>
<div className="text-div">
<Typography textAlign="center" variant="h3">Create a new account</Typography>
</div>
<Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
{ error && <Alert severity="error">{error}</Alert> }
<TextField label="Email" margin="dense" type="email" onChange={ (e) => {
setEmail(e.target.value);
}}/>
<TextField label="Password" margin="dense" type="password" onChange={ (e) => {
setPassword(e.target.value);
}}/>
<TextField label="Confirm Password" margin="dense" type="password" onChange={ (e) => {
setConfirmPassword(e.target.value);
}}/>
<Button onClick={signup} variant="contained" sx={{ marginTop: 2, }}>Sign Up</Button>
<div>
Already have an account? <Link to="/login" style={{ color: '#000' }}>Log In</Link>
</div>
</Container>
</>
)
}
export default Signup;
Login.js
import './Login.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth }from '../../../firebase';
import { Typography, Container, TextField, Button, Alert } from '#mui/material';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const history = useHistory();
const login = async () => {
try {
const user = await signInWithEmailAndPassword(
auth,
email,
password
);
//alert("Success, user is recognized");
history.push("/dashboard/canvas");
} catch (err) {
setError("The email or password you entered is incorrect");
}
}
return (
<>
<div className="text-div">
<Typography textAlign="center" variant="h3">Login</Typography>
</div>
<Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
{ error && <Alert severity="error">{error}</Alert> }
<TextField label="Email" margin="dense" type="email" onChange={(e) => {
setEmail(e.target.value);
}}/>
<TextField label="Password" margin="dense" type="password" onChange={(e) => {
setPassword(e.target.value);
}}/>
<Button onClick={login} variant="contained" sx={{ marginTop: 2, }}>Login</Button>
<div>
Don't have an account? <Link to="/signup" style={{ color: '#000' }}>Create one here</Link>
</div>
<div>
<Link to="/request-password-reset" style={{ color: '#000' }}>Forgot your password?</Link>
</div>
</Container>
</>
)
}
export default Login;
firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey,
authDomain,
projectId,
storageBucket,
messagingSenderId,
appId,
measurementId
}
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export {
auth
};
App.js
import './App.css';
import Home from './components/Pages/Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Signup from './components/Pages/Signup/Signup';
import Login from './components/Pages/Login/Login';
import UserDashboard from './components/Pages/UserDashboard/UserDashboard';
import ForgotPassword from './components/Pages/Forgot-Password';
function App() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/signup" component={Signup}/>
<Route path="/login" component={Login}/>
<Route path="/dashboard" component={UserDashboard}/>
<Route path="/request-password-reset" component={ForgotPassword}/>
</Switch>
</div>
</Router>
);
}
export default App;
If you are trying to create a private route component without persisting the authentication state somewhere in your app and exposed out via a React context then you will need to check the auth status asynchronously on each route change. This means you'll also need a "loading" or "pending" state while the auth status check occurring.
Here's an example implementation of just a custom private route sans any persisted state.
import { useEffect, useState } from 'react';
import { Route, Redirect } from 'react-router-dom'; // v4/5
import { onAuthStateChanged } from 'firebase/auth';
import { auth }from '../../../firebase';
const PrivateRoute = props => {
const [pending, setPending] = useState(true);
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubscribe = onAuthStateChanged(
auth,
user => {
setCurrentUser(user);
setPending(false);
},
error => {
// any error logging, etc...
setPending(false);
}
);
return unsubscribe; // <-- clean up subscription
}, []);
if (pending) return null; // don't do anything yet
return currentUser
? <Route {...props} /> // <-- render route and component
: <Redirect to="/login" />; // <-- redirect to log in
};
react-router-dom#6
Custom route components are out in v6, use a layout route. The PrivateRoute component will replace Route with Outlet for nested routes to render their matched element prop into, and Navigate replaces Redirect.
import { useEffect, useState } from 'react';
import { Outlet, Navigate } from 'react-router-dom'; // v4/5
import { onAuthStateChanged } from 'firebase/auth';
import { auth }from '../../../firebase';
const PrivateRoute = props => {
const [pending, setPending] = useState(true);
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubscribe = onAuthStateChanged(
auth,
user => {
setCurrentUser(user);
setPending(false);
},
error => {
// any error logging, etc...
setPending(false);
}
);
return unsubscribe; // <-- clean up subscription
}, []);
if (pending) return null; // don't do anything yet
return currentUser
? <Outlet /> // <-- render outlet for routes
: <Navigate to="/login" replace />; // <-- redirect to log in
};
Wrap the routes you want to protect.
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/request-password-reset" element={<ForgotPassword />} />
<Route element={<PrivateRoute />}>
<Route path="/dashboard" element={<UserDashboard />} />
</Route>
</Routes>
</Router>
);
}
my app.jsx
import axios from "axios";
import { createContext, useState } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import NavBar from "./components/Navbar";
import ProtectedRoute from "./components/ProtectedRoute";
import Home from "./pages/Home";
import Login from "./pages/Login";
import MyBlogs from "./pages/MyBlogs";
import Profile from "./pages/Profile";
import Signup from "./pages/Signup";
export const AuthContext = createContext();
const App = () => {
const [loggedIn, setLoggedIn] = useState(
Boolean(sessionStorage.getItem("loggedIn"))
);
const VUserData = sessionStorage.getItem("userData");
const [userData, setUserData] = useState(
VUserData ? JSON.parse(VUserData) : null
);
function getUserData() {
return axios
.get("/api/user")
.then((res) => {
return Promise.resolve(res.data);
})
.catch((err) => {
return Promise.reject(err.response);
});
}
return (
<BrowserRouter>
<AuthContext.Provider
value={{
loggedIn,
setLoggedIn,
userData,
setUserData,
getUserData,
}}
>
<Routes>
<Route path="/" element={<NavBar />}>
<Route index element={<Home />} />
<Route path="login" element={<Login />} />
<Route path="signup" element={<Signup />} />
<Route
path="profile"
element={
<ProtectedRoute>
<Profile />
</ProtectedRoute>
}
/>
<Route
path="my-blogs"
element={
<ProtectedRoute>
<MyBlogs />
</ProtectedRoute>
}
/>
</Route>
</Routes>
</AuthContext.Provider>
</BrowserRouter>
);
};
export default App;
my ProtectedRoute.jsx
import { useContext } from "react";
import { Navigate, useLocation } from "react-router-dom";
import { AuthContext } from "../app";
const ProtectedRoute = ({ children, ...rest }) => {
const { loggedIn, getUserData } = useContext(AuthContext);
const location = useLocation();
if (loggedIn) {
getUserData()
.then((res) => {
return children;
})
.catch((res) => {
return (
<Navigate
to="/login"
replace
state={{ path: location.pathname }}
/>
);
});
} else {
return (
<Navigate to="/login" replace state={{ path: location.pathname }} />
);
}
};
export default ProtectedRoute;
my home.jsx
import React from "react";
import { Outlet } from "react-router-dom";
import NavBar from "../components/Navbar";
const Home = () => {
return (
<React.Fragment>
<NavBar />
<Outlet />
</React.Fragment>
);
};
export default Home;
My problem is after successful login, the profile page not rendering. It's just a blank white page but if I replacegetUserData() and the whole promise resolve with just return children; it's in ProtectedRoute.jsx it's working fine why is that?
how could I check whether the user is logged or not by requesting to the backend in ProtectedRoute.jsx
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;
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>