In here, the data successfully Stored message shows. But data is not showing in the mongo DB database. What is the trouble with it? - frontend

In here, the data successfully Stored message shows. But data is not showing in the mongo DB database. What is the trouble with it?
I'm creating a simple cruds application using React.js and node.js, connecting using Axios. I use DB as mongo DB. This is the data insertion react.js file.
const onSubmit = () => {
const data = {
name: name,
age: age,
city: cityArray,
};
axios.post("http://localhost:5000/student", data).then((res) => {
if (res.data.success) {
alert("Added Succes");
window.location = "/";
setAge("");
setCity("");
setName("");
}
});
};

What is the Exact Error?
Did you Implement the PreventDefault function?
if not, please use the following code,
e.prventDefault();
in the onSubmit function ,do the following changes
const onSubmit = (e) => {
e.prventDefault();
const data = {
name: name,
age: age,
city: cityArray,
};

Please refer this you can get clear idea
import React from 'react';
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import AddPosts from './components/AddPosts.jsx';
import Posts from './components/Posts.jsx';
import UpdatePosts from './components/UpdatePosts.jsx';
import UserRegister from './components/UserRegister';
import UserLogin from './components/UserLogin';
export function App() {
return(
<div>
<BrowserRouter>
<Routes>
<Route path="/" element={ <UserLogin/>}/>
<Route path="/user/register" element={ <UserRegister/>}/>
<Route path="/add" element={<AddPosts/>}></Route>
<Route path="/view" element={<Posts/>}></Route>
<Route path="/update/:id" element={<UpdatePosts/>}></Route>
</Routes>
</BrowserRouter>
</div>
);
}

Related

User disconnected for a few seconds when reloaded using AuthContext [duplicate]

This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed last month.
I am building an app using Node.js v18 and React v18 and I have the following problem.
I am redirecting my user when he tries to go to login but is already logged in. The problem is that for a short second, the login page is rendered.
AuthContext.tsx
import { User, getAuth, onAuthStateChanged } from "firebase/auth";
import React, { useState, useEffect, createContext, PropsWithChildren} from 'react';
export const AuthContext = createContext<User | null>(null);
export const AuthProvider = (props : PropsWithChildren) => {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
onAuthStateChanged(getAuth(), (currentUser) => {
setUser(currentUser);
})
}, []);
return <AuthContext.Provider value={user}>{props.children}</AuthContext.Provider>
}
App.tsx
import React, { useContext } from "react";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import LoginPage from "./authentication/LoginPage";
import RegisterPage from "./authentication/RegisterPage";
import { AuthContext } from "./context/AuthContext";
import PaymentPage from "./payment/PaymentPage";
export default function App(){
const user = useContext(AuthContext);
function redirect(){
if(user !== null){
return <Navigate to="/pay" />;
}else{
return <LoginPage />;
}
}
return (
<BrowserRouter>
<Routes>
<Route path="/" element={redirect()}/>
<Route path="/register" element={<RegisterPage />}/>
<Route path="/pay" element={<PaymentPage />}/>
</Routes>
</BrowserRouter>
);
}
Any ideas on how to solve this problem ?
Thank you
Firebase automatically restores the user credentials from the local storage of the device, but during this process is makes a call to the server to check if those credentials are still valid, for example if the account hasn't been suspended. The onAuthStateChanged listener won't fire its first event until this asynchronous call has completed, which may indeed take a moment. Your UI uses the lack of a current user as a sign to show the login UI, which causes that UI to show briefly.
Typically you'll want to show some loading indicator while the check is going on, until you get the first onAuthStateChanged event. This is the simplest solution as you now handle all three cases: the user is not logged in, the user is logged in, and when we don't know yet whether the user is logged in.
Alternatively, you can store a value in local storage to indicate that the user was logged in before, and based on that assume that they will be logged in successfully again as Michael Bleigh showed in this I/O talk. Note though that in this case there's a change that your app thinks the user is logged in initially, but then turns out to be wrong - for example when the account was suspended - so you'll have to handle this flow by then redirecting back to the log-in page.
Thanks to #Frank van Puffelen I managed to implement a solution :
AuthContext.tsx
import { User, getAuth, onAuthStateChanged } from "firebase/auth";
import React, { useState, useEffect, createContext, PropsWithChildren} from 'react';
export const AuthContext = createContext<UserLogged>({user : null, loading: true});
//Add an interface to store the loading state
interface UserLogged {
user : User | null ;
loading : boolean;
}
export const AuthProvider = (props : PropsWithChildren) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
onAuthStateChanged(getAuth(), (currentUser) => {
setUser(currentUser);
//once the user has been set I now the loading is over
setLoading(false);
})
}, []);
return <AuthContext.Provider value={{user: user, loading: loading}}>{props.children}</AuthContext.Provider>
}
App.tsx
import React, { useContext } from "react";
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import LoginPage from "./authentication/LoginPage";
import RegisterPage from "./authentication/RegisterPage";
import { AuthContext } from "./context/AuthContext";
import PaymentPage from "./payment/PaymentPage";
import { Audio } from 'react-loader-spinner'
export default function App(){
const userLogged = useContext(AuthContext);
function redirect(){
if(userLogged.loading){
// add a loading indicator
return <Audio />;
}else{
if(userLogged.user !== null){
return <Navigate to="/pay" />;
}else{
return <LoginPage />;
}
}
}
return (
<BrowserRouter>
<Routes>
<Route path="/" element={redirect()}/>
<Route path="/register" element={<RegisterPage />}/>
<Route path="/pay" element={<PaymentPage />}/>
</Routes>
</BrowserRouter>
);
}

socket.io broadcasting not working with React

I am currently trying to build a connection between a Node.js application in the backend and a React application in the frontend. The connection from the frontend to the backend seems to work without any problems. Unfortunately, the React application, on the other side, cannot accept any data.
The socket.on(...) function throws an error:
dashboard.js:20 Uncaught TypeError: Cannot read properties of null (reading 'on')
I can not explain where the error lies.
app.js (mounting point of the React app):
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import io from 'socket.io-client';
import Dashboard from "./compontents/views/dashboard/dashboard";
function App() {
const [socket, setSocket] = useState(null);
useEffect(() => {
const newSocket = io(`http://${window.location.hostname}:8040`);
setSocket(newSocket);
return () => newSocket.close();
}, [setSocket]);
return (
<Router>
<div className="app">
<div className="app__view">
<Switch>
<Route exact path="/">
<Dashboard socket={socket} />
</Route>
</Switch>
</div>
</div>
</Router>
);
}
export default App;
dashboard.js (child component):
import React, { useEffect, useState } from 'react';
import { Link } from "react-router-dom";
import FeatherIcon from 'feather-icons-react';
import LargeButton from "../../buttons/largeButton/largeButton";
function Dashboard({ socket }) {
function toggleLight(type) {
if(type) {
// this function works fine
socket.emit("toggle light", type);
console.log(type);
}
}
useEffect(() => {
// this line is causing the error
socket.on('toggle button', (type) => {
console.log(type);
});
}, [socket]);
return(
<div className="view">
<div className="all">
<LargeButton icon="sun" text="Alles einschalten" event={toggleLight} />
<LargeButton icon="moon" text="Alles ausschalten" event={toggleLight} />
</div>
</div>
)
}
export default Dashboard;
It seems like your <Dashboard/> component are mounting before the socket instance are ready to go. Socket connection is an a async procedure so you must take this on mind when you use it.
Try change your app.js to this:
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import io from 'socket.io-client';
import Dashboard from './compontents/views/dashboard/dashboard';
function App() {
const [socket, setSocket] = useState(null);
useEffect(() => {
const newSocket = io(`http://${window.location.hostname}:8040`);
setSocket(newSocket);
return () => newSocket.close();
}, [setSocket]);
if (!socket) {
// catch and show some loading screen
// while the socket connection gets ready
return <div>Loading...</div>;
}
return (
<Router>
<div className="app">
<div className="app__view">
<Switch>
<Route exact path="/">
<Dashboard socket={socket} />
</Route>
</Switch>
</div>
</div>
</Router>
);
}

How can I keep the query string in address bar while avoiding redirecting on form submission?

I am trying to use the Spotify Web Api in a MERN application using this package.
When the user enters an artist in the search form, I'd like to make a get request with the search value in the query string to use in my Express route handling.
The way I have it set up currently is working as expected, except for the fact that I'd like the query string to be in the address bar. With preventDefault() in the handleSubmit() method it does not get added.
When I remove preventDefault() the query string is present, but only after redirecting, so I cannot save the response in state.
Is there a way to fix this? Or a better way to accomplish this?
Search component:
import React from 'react';
import axios from 'axios';
class Search extends React.Component {
state = {}
handleSubmit(e) {
e.preventDefault();
axios({
method: "GET",
url: `http://localhost:8000/spotify/search?artist=${this.state['artist']}`,
}).then(res => this.setState({ searchData: res.data }));
}
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value
});
}
render() {
return (
<form onSubmit={(event) => this.handleSubmit(event)}>
<input name={"artist"} value={this.state['artist']} onChange={(e) => this.handleChange(e)}></input>
<button>Submit</button>
</form>
)
}
}
export default Search;
Express route handler
router.get('/search', (req,res) => {
spotifyApi.searchArtists(req.query["artist"]).then(function(data) {
res.send(data.body);
}, function(err) {
console.error(err);
});
})
App.js Router
<Router>
<Switch>
<Route path="/spotify/user" component={User} />
<Route path="/spotify/search" component={Search} />
</Switch>
</Router>

How to use useEffect and the Context API to check if a user is logged in and protect a route?

I am trying to protect routes based on whether a user is logged in or not but I cannot get this to work properly since it seems that the information stored in my context provider is not available on the initial component load.
I am checking whether the user is authenticated within my App.js file by making a request to my node server through the useEffect hook. It tries to store this info within the context api which it successfully does but it appears that rendering other components will not wait for the context api to "catch up" or load first.
I am sure there is something simple I am missing or maybe I am using bad convention with checking if a user is authenticated. Any help would be appreciated!
App.js
import React, { useState, useEffect } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Axios from 'axios';
import Header from './components/layout/Header';
import Home from './components/pages/Home';
import HiddenPage from './components/pages/HiddenPage';
import Login from './components/auth/Login';
import Register from './components/auth/Register';
import UserContext from './context/UserContext';
import ProtectedRoute from './components/auth/ProtectedRoute';
import './style.scss';
export default function App() {
const [userData, setUserData] = useState({
token: undefined,
user: undefined,
});
useEffect(() => {
const checkLoggedIn = async () => {
let token = localStorage.getItem('auth-token');
if (token === null) {
localStorage.setItem('auth-token', '');
token = '';
}
const tokenResponse = await Axios.post(
'http://localhost:5000/users/tokenIsValid',
null,
{ headers: { 'x-auth-token': token } }
);
if (tokenResponse.data) {
const userResponse = await Axios.get('http://localhost:5000/users/', {
headers: { 'x-auth-token': token },
});
setUserData({
token,
user: userResponse.data,
});
}
};
checkLoggedIn();
}, []);
return (
<>
<BrowserRouter>
<UserContext.Provider value={{ userData, setUserData }}>
<Header />
<div className="container">
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
<ProtectedRoute path="/hidden" component={HiddenPage} />
</Switch>
</div>
</UserContext.Provider>
</BrowserRouter>
</>
);
}
ProtectedRoute.js
import React, { useContext } from 'react';
import { Redirect } from 'react-router-dom';
import UserContext from '../../context/UserContext';
export default function ProtectedRoute(props) {
const { userData } = useContext(UserContext);
const Component = props.component;
const isAuthenticated = !!userData.user;
console.log(isAuthenticated);
return isAuthenticated ? <Component /> : <Redirect to={{ pathname: '/' }} />;
}
add a loading state...
const [loading, setLoading] = useState(false)
after you check local storage and make axios call, update the state
if (loading) return null;
// else render the routes
return (
// the regular routes...
)
You basically want to account for a 3rd state.
Either:
there is a user
there is NO user
the effect has not yet completed
At whatever stage in your useEffect you can confirm that there is no user (unauthenticated) set the user to false.
Since undefined !== false, in your return you can test for both...
if (userData.user === undefined) return 'loading...'
Then you can use your ternary after this line with the knowledge that user has some value. Either false or some user object...

How to effectively protect routes in combination with react-router and passport on the backend

I have React and Node.js with passport.js on the backend which implements my app auth. My react makes a call to my backend and fetches the authorized user via action reducer. Everything works fine but there is a problem with the route guards. This is how I am protecting the routes if the user is not logged in
if(!this.props.auth) return
The problem is when the user is logged in, if page is refreshed, the code above executes faster than mapStateToProps returns the authorized user and the loggedIn user is redirected to the index page. This is bad user experience. Please help me how to resolve this issue and I would appreciate help and advice.
I think what I need to do is to ensure that store is updated first before DOM is rendered but I am not sure how to do it.
Here is dashboard
class Dashboard extends Component {
render() {
if(!this.props.auth) return <Redirect to='/' />
if (!this.props.auth.googleUsername) {
return <div className='container'> Loading ... </div>;
} else {
return (
<div className='container' style={{ margin: '10px 10px' }}>
{this.props.auth.googleUsername}
</div>
);
}
function mapStateToProps({auth}) {
return {auth};
}
export default connect(mapStateToProps)(Dashboard);
Here is App.js
import { connect } from 'react-redux';
import { fetchUser } from './store/actions/index';
import Home from './components/layout/Home';
import Dashboard from './components/layout/Dashboard';
class App extends Component {
componentDidMount() {
this.props.fetchUser();
}
render() {
return (
<div>
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path='/' component={Home} />
<Route path='/dashboard' component={Dashboard} />
</Switch>
</div>
</BrowserRouter>
</div>
);
}
}
export default connect(null,{ fetchUser })(App)
Action reducer
import axios from 'axios';
import { FETCH_USER } from './types';
export const fetchUser = () => async dispatch => {
const res = await axios.get('/api/current_user');
dispatch({ type: FETCH_USER, payload: res.data });
};
Auth Reducer
import { FETCH_USER } from '../actions/types';
export default function(state = false, action) {
switch (action.type) {
case FETCH_USER:
return action.payload;
default:
return state;
}
}
For those who has this issue, I managed to solve the probable. The issue was that I need to persist redux store across my app. I used a third party library called 'redux-persist'
Here is the set I used in my index.js
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './store/reducers/rootReducer';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { PersistGate } from 'redux-persist/integration/react';
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(persistedReducer, applyMiddleware(thunk));
const persistor = persistStore(store);
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root'));

Resources