React form is putting text field values into query strings? - javascript

For some reason upon clicking the submit button on a login form in my React tutorial, the URL changes from domain.com/login to domain.com/login?email=max%40test.com&password=admin123 putting the email and password field into the URL.
The instructor's example doesn't seem to do this.
LoginScreen.js
import { useState } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { useDispatch } from 'react-redux'
import { loginAction } from '../state/actions/userActions'
const LoginScreen = () => {
const [email, setEmail] = useState('')
const [password, setPW] = useState('')
const location = useLocation()
const dispatch = useDispatch()
const submitLoginHandler = (e) => {
e.preventDefault()
dispatch(loginAction(email, password))
}
return (
<div className="full-container w-full">
<div className="login-content-container max-w-5xl mx-auto p-16 my-8">
<div className="login-container flex items-center bg-white shadow-2xl rounded-md overflow-hidden">
<div className="form-container rounded-md p-8 my-16">
<h2 className="my-1 font-medium text-center text-2xl">sevenTwigs</h2>
<span className="mb-8 block text-center font-light text-md">Account Login</span>
<form className="login-form p-4">
<div className="input-group flex flex-col items-center">
<input
className="email-input border-2 border-gray-200 placeholder-gray-400 rounded-md px-4 py-2 m-2 w-full text-sm font-light"
type="email"
name="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)} />
<input
className="password-input border-2 border-gray-200 placeholder-gray-400 rounded-md px-4 py-2 m-2 w-full text-sm font-light"
type="password"
name="password"
placeholder="Your password"
value={password}
onChange={(e) => setPW(e.target.value)} />
<button
type="submit"
className="w-full rounded-sm px-4 py-2 bg-pink-400 hover:bg-pink-300 hover:shadow-md disabled:bg-gray-400
text-white shadow-sm text-center text-sm my-4"
value="submit"
onClick={(e) => submitLoginHandler(e)} >
LOG IN
</button>
</div>
</form>
<div className="new-user-register text-sm text-center text-gray-400">
Don't have an account?
<Link to="/register/" className="text-gray-500"> Register</Link>
</div>
</div>
<div className="form-splash-image bg-black w-2/3 hidden md:flex">
<img src="/images/wallpainting.jpg" alt="" />
</div>
</div>
</div>
</div>
)
}
export default LoginScreen
app.js
import {BrowserRouter as Router, Route } from 'react-router-dom'
import Header from './components/Header'
import Footer from './components/Footer'
import HomeScreen from './screens/HomeScreen'
import ProductScreen from './screens/ProductScreen'
import CartScreen from './screens/CartScreen'
import CheckoutScreen from './screens/CheckoutScreen'
import LoginScreen from './screens/LoginScreen'
function App() {
return (
<Router>
<Header />
<div className="main-content h-full min-h-screen flex" style={{backgroundColor: '#f5fcf7'}}>
<Route path="/" component={HomeScreen} exact />
<Route path="/products/:id" component={ProductScreen} />
<Route path="/cart" component={CartScreen} />
<Route path="/checkout" component={CheckoutScreen} />
<Route path="/login" component={LoginScreen} />
</div>
<Footer />
</Router>
);
}
export default App;
Backend Express Routes
const express = require('express')
const router = express.Router()
const { userAuth, registerUser, retrieveProfile } = require('../controller/userController')
const { protect } = require('../middleware/authTokenMW')
router.post('/login', userAuth)
router.post('/register', registerUser)
router.route('/profile')
.get(protect, retrieveProfile)
module.exports = router
server.js
const express = require('express')
const dotenv = require('dotenv')
const mongoose = require('mongoose')
const connectDB = require('./config/db')
const products = require ('./data/products')
const productRoutes = require('./routes/productRoutes')
const userRoutes = require('./routes/userRoutes')
const { notFound, errorHandler } = require('./middleware/errorHandlerMW')
const app = express()
dotenv.config()
connectDB()
app.use(express.json())
// Start Server
app.listen(5000, () =>{
console.log('Server started')
})
// Route for Products
app.use('/api/products', productRoutes)
// Route for Users
app.use('/api/users/', userRoutes)
// Error Handlers
app.use(notFound)
app.use(errorHandler)
Is putting text field values into query strings in the URL done by React on default? Or did I mess with something inadvertently?

Your button is of type submit and you have not specified any properties for the form. So by default, it is going to assume a few things,
method="Get" and
action="Your URL"
So it adds all params to your URL as query string params.
And adding the submit event to the form will solve the issue as you have e.preventDefault there.

If you pass event as a parameter on function call, the e.preventDefault() will not work.
Try to use just
onClick={submitLoginHandler}
instead of
onClick={(e) => submitLoginHandler(e)}

Related

React Query Hook Not Running On Initial Load

Here is my React component. The hook I am trying to get to work is useGetNotesQuery.
On initial load of the component, it does not run. If I tab switch, or create a new note, then the hook will be called and the component will update. I don't have this problem with other hooks and I can't seem to figure out what I am doing wrong. I'm still pretty new to react-query. I've verified that when the hook is called on tab switch or the creation of a new note, the correct data is being passed in and the hook works as expected. It just will not run on initial load.
import React, { useState } from 'react';
import useGetNotesQuery from 'hooks/notes/useNotes';
import { useCreateNote } from 'hooks/notes/useCreateNote';
import Note from './Note';
export default function Notes({ projectItemId, itemId, itemType }: any) {
const createNote = useCreateNote();
const {
data: notes,
isLoading,
isError
} = useGetNotesQuery({ projectItemId, itemId, itemType });
const [body, setBody] = useState('');
const createNewNote = async () => {
await createNote.mutateAsync({
body,
projectItemId,
itemId,
itemType
});
setBody('');
};
return (
<section aria-labelledby="notes-title">
{!isLoading && (
<div className="sm:overflow-hidden sm:rounded-lg">
{console.log(notes)}
<div className="divide-y divide-gray-200">
<div className="px-4 py-5 sm:px-6">
<h2
id="notes-title"
className="text-lg font-medium text-gray-900"
>
Notes
</h2>
</div>
<div className="px-4 py-6 sm:px-6">
<ul role="list" className="space-y-8">
{notes?.map((note) => (
<Note key={note.id} note={note} />
))}
</ul>
</div>
</div>
<div className="bg-gray-50 px-4 py-6 sm:px-6">
<div className="flex space-x-3">
{/* <div className="flex-shrink-0">
<img
className="h-10 w-10 rounded-full"
src={user.imageUrl}
alt=""
/>
</div> */}
<div className="min-w-0 flex-1">
<form action="#">
<div>
<label htmlFor="comment" className="sr-only">
About
</label>
<textarea
id="comment"
name="comment"
rows={3}
className="block w-full p-2 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm text-black"
placeholder="Add a note"
value={body}
onChange={(e) => setBody(e.target.value)}
/>
</div>
<div className="mt-3">
<button
type="submit"
onClick={(e) => {
e.preventDefault();
createNewNote();
}}
className="inline-flex items-center justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
Comment
</button>
</div>
</form>
</div>
</div>
</div>
</div>
)}
</section>
);
}
Here is the hook
import { getNotesById } from './../../queries/notes/get-notes';
import { useQuery } from 'react-query';
function useGetNotesQuery({ projectItemId, itemId, itemType }: any) {
return useQuery('notes', async () => {
if (projectItemId) {
return getNotesById({ projectItemId, itemId, itemType }).then(
(result) => result.data
);
}
});
}
export default useGetNotesQuery;
My _app.tsx file in NextJS
import 'styles/main.css';
import 'styles/chrome-bug.css';
import '#/styles/tailwind.css';
import 'katex/dist/katex.css';
import '../styles/globals.css';
import 'react-datepicker/dist/react-datepicker.css';
import { useEffect, useState } from 'react';
import React from 'react';
import { SessionContextProvider } from '#supabase/auth-helpers-react';
import { createBrowserSupabaseClient } from '#supabase/auth-helpers-nextjs';
import { AppProps } from 'next/app';
import { MyUserContextProvider } from 'utils/useUser';
import type { Database } from 'types_db';
import { SidebarProvider } from 'context/SidebarContext';
import { QueryClient, QueryClientProvider } from 'react-query';
// import { ReactQueryDevtools } from 'react-query/devtools';
import { ThemeProvider } from 'next-themes';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 3
}
}
});
export default function MyApp({ Component, pageProps }: AppProps) {
const [initialContext, setInitialContext] = useState();
const [supabaseClient] = useState(() =>
createBrowserSupabaseClient<Database>()
);
useEffect(() => {
document.body.classList?.remove('loading');
}, []);
return (
<QueryClientProvider client={queryClient}>
<SessionContextProvider supabaseClient={supabaseClient}>
<MyUserContextProvider initial={initialContext}>
<SidebarProvider>
<ThemeProvider
attribute="class"
enableColorScheme={false}
defaultTheme="light"
>
<Component {...pageProps} />
</ThemeProvider>
{/* <ReactQueryDevtools initialIsOpen={false} /> */}
</SidebarProvider>
</MyUserContextProvider>
</SessionContextProvider>
</QueryClientProvider>
);
}
What do your other hooke return? Your query getNotesById is returning result.data, but the useQuery structure expects a parsed result object, like the example below:
const fetchUsers = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
return res.json();
};
const response = useQuery("users", fetchUsers);

Changing background based on weather description using next.js

I am trying to have it so that the background of my weather app will change based on the weather condition using nextjs and the current weather data API. But no image is display. This is my Main
import axios from "axios";
import { useState } from "react";
import Weather from "./Weather";
import Spinner from "./Spinner";
import { data } from "autoprefixer";
import Background from "./Background";
const Main = () => {
const [city, setCity] = useState("");
const [weather, setWeather] = useState({});
const [loading, setLoading] = useState(false);
//In celsius -(&units=metric) in fahrenheit -(&units=imperial)
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${process.env.NEXT_PUBLIC_WEATHER_KEY}`;
const fetchWeather = (e) => {
e.preventDefault();
setLoading(true);
axios.get(url).then((response) => {
setWeather(response.data);
// console.log(response.data);
});
setCity("");
setLoading(false);
};
if (loading) {
return <Spinner />;
} else {
return (
<div>
{/* Overlay, so we use a self-closing div */}
<div className="absolute top-0 left-0 right-0 bottom-0 bg-black/40 z-[1]" />
<Background
weatherDescription={data.weather ? data.weather[0].main : null}
/>
<div className="relative flex justify-between items-center max-w-[500px] w-full m-auto pt-4 text-white z-10">
<form
onSubmit={fetchWeather}
className="flex justify-between items-center w-full m-auto p-3 bg-transparent border border-gray-300 text-white rounded-2xl"
>
<div>
<input
onChange={(e) => setCity(e.target.value)}
className="bg-transparent border-none text-white focus:outline-none text-2xl"
type="text"
placeholder="Search"
/>
</div>
<button onClick={fetchWeather}>
<BsSearch size={20} />
</button>
</form>
</div>
{weather.main && <Weather data={weather} />}
</div>
);
}
};
And this is the background component
import React from "react";
import img1 from "../public/assets/Rainy weather.jpg";
import img2 from "../imgs/Cloudy Weather.jpg";
function Background(props) {
const images = [
{
name: "Rain",
background: img1,
},
{
name: "Clouds",
background: img2,
},
];
const imgURL = images.find((el) => el.name === props.weatherDescription)
?.background;
return (
<div className="-z-10">
<img
src={imgURL}
className="object-cover w-full min-h-scren"
key={imgURL}
/>
</div>
);
}
export default Background;
I've tried if statements and functions, but nothing is working, I've found this current template that was working with videos, I've tried to change it to use images, but I can't achieve it. I'm fairly new in coding, so I hope someone can help me.

How to redirect to another page using on keypress in nextjs?

I need to redirect users using onkeypress in nextjs.
I have a search input where users can type and then press enter key to go to the other page.
What I've tried:
const handler = (e) => {
const ENTER = 13;
if (e.keyCode === ENTER) // do a history.push("/news");
console.log("enter");
};
<input
onKeyPress={(e) => handler(e)}
type="text"
name="search"
placeholder="Search by keywords"
className="p-4 md:p-6 w-full py-2 md:py-4 border-2 text-lg md:text-2xl xl:text-3xl border-gray-400 outline-none filosofia_italic bg-white placeholder-gray-400"
/>
i would appreciate your help.
This is covered by the documentation:
Imperatively
next/link should be able to cover most of your routing needs, but you
can also do client-side navigations without it, take a look at the
documentation for next/router.
The following example shows how to do basic page navigations with
useRouter:
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Click here to read more
</button>
)
}
You can use the useRouter hook from next - https://nextjs.org/docs/api-reference/next/router
Try changing you handler to
import {useRouter} from "next/router";
const Component = () => {
const router = useRouter();
const handler = (e) => {
...
router.push("/news");
}
return (
<input
onKeyPress={handler}
type="text"
name="search"
placeholder="Search by keywords"
className="p-4 md:p-6 w-full py-2 md:py-4 border-2 text-lg
md:text-2xl xl:text-3xl border-gray-400 outline-none
filosofia_italic bg-white placeholder-gray-400"
/>
)
}

Unhandled Rejection (TypeError): setToken is not a function

I am having trouble with React while trying to set a token for user to log into the application. I followed the steps here to create a login process but it gives out the Unhandled Rejection (TypeError): setToken is not a function error and I am not able to correct it.
Here are the associated codes starting with from app.js
import React from 'react';
import { useState } from 'react';
import Login from './Pages/Login.js';
import Signup from './Pages/Signup';
import Dashboard from './Pages/Dashboard.js';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import useToken from './Pages/useToken'
function App() {
const { token, setToken } = useToken();
if (!token) {
return <Login setToken={setToken} />
};
return (
<div className="">
<BrowserRouter>
<Switch>
<Route exact path="/">
<Login />
</Route>
<Route exact path="/dashboard">
<Dashboard />
</Route>
<Route exact path="/signup">
<Signup />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
Login.js
import React from 'react';
import { useState } from 'react';
import PropTypes from 'prop-types';
async function loginUser(credentials) {
return fetch('http://localhost:8080/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
const Login = ({setToken}) => {
// username is email address
const [username, setUsername] = useState();
const [password, setPassword] = useState();
const handleSubmit = async(e) => {
e.preventDefault();
const token = await loginUser({
username,
password
});
setToken(token);
};
return (<>
<section>
<div className="flex items-center justify-center flex-col">
<div className="w-11/15 mt-24 rounded-lg flex flex-col items-center justify-center relative">
<span className="flex flex-col lg:flex-row">
<header className="text-5xl pr-4">Welcome to </header>
<header className="font-LoveloLine text-6xl font-bold text-center"> Mused</header>
</span>
</div>
</div>
</section>
<section>
<div className="flex items-center justify-center flex-col">
<div className=" lg:w-2/4 px-4 rounded-lg flex flex-col items-center justify-center relative" >
<div className="py-12">
<form action="" className="flex flex-col gap-2" onSubmit={handleSubmit}>
{/* Email address */}
<label htmlFor=""></label>
<input className="w-96 text-brand-Black bg-white border-brand-Blue rounded-lg py-3 focus:outline-none focus:ring-2 focus:ring-brand-Red focus:border-transparent" type="email" name="" placeholder="Email address" id="" onChange={(e) => { setUsername(e.target.value)}} />
<label htmlFor=""></label>
{/* Password */}
<input className="w-96 bg-white text-brand-Black border-brand-Blue rounded-lg py-3 focus:outline-none focus:ring-2 focus:ring-brand-Red focus:border-transparent" type="password" name="" placeholder="Password" id="" onChange={(e) => { setPassword(e.target.value)}} />
<div class="max-w-sm mx-auto py-4">
<label class="inline-flex items-center">
<input class="text-brand-Blue w-6 h-6 mr-2 focus:ring-indigo-400 focus:ring-opacity-25 border border-gray-300 rounded" type="checkbox" />
Remember me
</label>
</div>
{/* Login button */}
<div>
<button type="submit" className="bg-brand-Blue rounded-full w-full py-2 active:bg-brand-Blue-dark hover:bg-brand-Blue-dark">Login</button>
</div>
</form>
{/* Checkbox */}
{/* Signup button */}
<div className="flex flex-col items-center justify-center ">
<p className="py-6">or</p>
<button type="submit" className="bg-brand-Red rounded-full w-full py-2 active:bg-brand-Red-dark hover:bg-brand-Red-dark">Signup
</button>
</div>
</div>
</div>
</div>
</section>
</>
)
};
Login.propTypes = {
setToken: PropTypes.func.isRequired,
}
export default Login
Error is thrown for Login.js file "setToken(token)" function. It is not supposed to be a function
import { useState } from 'react';
const useToken = () => {
const getToken = () => {
const tokenString = localStorage.getItem("token");
const userToken = JSON.parse(tokenString);
return userToken?.token
};
const [token, setToken] = useState(getToken());
const saveToken = (userToken) => {
localStorage.setItem("token", JSON.stringify(userToken));
setToken(userToken.token);
}
return {
setToken: saveToken,
token
}
}
export default useToken
In my own case i discovered that I was passing the setToken function inside login route like this:
<Route path='/login setToken={setToken} />
instead of
<Route path='/login' exact element={<Login
setToken={setToken}
/>}
/>
You have to pass it to the component itself, not the route!
Your posting this as within only the component, as soon as you go to the route it disregards it. So from localhost:3000 it'll work fine, but because you've not passed it through the route setToken={token} it'll disregard it. Scratched my head at this for hours.

Why my redux state is go back to intial value on Page Refresh?

After SignIn, my authState becomes true, which is expected. and then It redirected to the home page. But after that If I refresh the page, my state goes back to initial value. what could be the reason of this?
Login Component:
import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import axios from 'axios';
import { Redirect } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { setCurrentUser } from 'src/store/reducer/authReducer';
import jwtDecode from 'jwt-decode';
import { authenticate, isAuth } from '../helpers/auth';
import Protection from '../assets/Protection.svg';
import useInputState from '../hooks/useInputState';
const Login = (props) => {
const auth = useSelector((state) => state.auth);
const [submitted, setSubmitted] = useState(false);
const [btnText, setBtnText] = useState('Sign In');
const dispatch = useDispatch();
const { location } = props;
const initialValue = { name: '', email: '', password: '' };
const [state, onChangeHandler, reset] = useInputState(initialValue);
const { email, password } = state;
// submit data to backend
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post(`${process.env.API_URL}/api/signin`, {
email,
password,
});
reset();
setSubmitted(true);
setBtnText('Submitted');
const { token: jwtToken } = res.data;
localStorage.setItem('jwtToken', jwtToken);
const decoded = jwtDecode(jwtToken);
dispatch(setCurrentUser(decoded));
} catch (err) {
const { errors } = err.response.data;
for (const error of errors) {
const msg = Object.values(error).toString();
toast.error(msg);
}
}
};
useEffect(() => {
if (location.message) {
toast.success(location.message);
}
}, [location.message]);
if (submitted && auth.isAuthenticated) {
return <Redirect to={{ pathname: '/', message: 'You are signed in' }} />;
}
return (
<div className="min-h-screen bg-gray-100 text-gray-900 flex justify-center">
<div className="max-w-screen-xl m-0 sm:m-20 bg-white shadow sm:rounded-lg flex justify-center flex-1">
<div className="lg:w-1/2 xl:w-5/12 p-6 sm:p-12">
<div className="mt-12 flex flex-col items-center">
<h1 className="text-2xl xl:text-3xl font-extrabold">
Sign In for MernAuth
</h1>
<form
className="w-full flex-1 mt-8 text-indigo-500"
onSubmit={handleSubmit}
>
<div className="mx-auto max-w-xs relative">
<input
className="w-full px-8 py-4 rounded-lg font-medium bg-gray-100 border-gray-200 placeholder-gray-500 text-sm focus:outline-none focus:border-gray-400 focus:bg-white mt-5"
type="email"
name="email"
placeholder="Email"
onChange={onChangeHandler}
value={email}
/>
<input
className="w-full px-8 py-4 rounded-lg font-medium bg-gray-100 border-gray-200 placeholder-gray-500 text-sm focus:outline-none focus:border-gray-400 focus:bg-white mt-5"
type="password"
name="password"
placeholder="Password"
onChange={onChangeHandler}
value={password}
/>
<button
className="mt-5 tracking-wide font-semibold bg-indigo-500 text-gray-100 w-full py-4 rounded-lg hover:bg-indigo-700 transition-all duration-300 ease-in-out flex items-center justify-center focus:shadow-outline focus:outline-none border-none outline-none"
type="submit"
>
<i className="fas fa-user-plus fa 1x w-6 -ml-2" />
<span className="ml-3">{btnText}</span>
</button>
</div>
<div className="my-12 border-b text-center">
<div className="leading-node px-2 inline-block text-sm text-gray-600 tracking-wide font-medium bg-white transform tranlate-y-1/2">
Or sign in with email or social login
</div>
</div>
<div className="flex flex-col items-center">
<a
className="w-full max-w-xs font-bold shadow-sm rounded-lg py-3
bg-indigo-100 text-gray-800 flex items-center justify-center transition-all duration-300 ease-in-out focus:outline-none hover:shadow focus:shadow-sm focus:shadow-outline mt-5"
href="/login"
target="_self"
>
<i className="fas fa-sign-in-alt fa 1x w-6 -ml-2 text-indigo-500" />
<span className="ml-4">Sign Up</span>
</a>
</div>
</form>
</div>
</div>
<div className="flex-1 bg-indigo-100 text-center hidden lg:flex">
<div
className="m-12 xl:m-16 w-full bg-contain bg-center bg-no-repeat"
style={{ backgroundImage: `url(${Protection})` }}
/>
</div>
</div>
</div>
);
};
export default Login;
my home component
import React, { useEffect } from 'react';
import { toast } from 'react-toastify';
const App = (props) => {
const { location } = props;
useEffect(() => {
if (location.message) {
toast.success(location.message);
}
}, [location.message]);
return <div>App</div>;
};
export default App;
I don't want the state to go back to its initial value on page refresh.
Your data is not persisted. Save the user token in the localStorage and when the app is initializing verify the localStorage and if there have value simply populate the store with this data.
Since you want to store user token, I would suggest that you use a cookie to store it, here is a link where you can find how to create and use a cookie.

Categories

Resources