React JS how would I render an alert inside of a function - javascript

I'm trying to make a login page and I want to display an error if the login fails.
I call the function here: onClick={ this.login }
But I want the function to display the error message without re-rendering everything. The function is inside a class

Try something like this:
const [username, setUsername] = useState('');
const [error, setError] = useState('');
const handleLogin = async () => {
// request a login to your server
const res = await fetch('http://yourapi.com/login');
// determine if the request was successful
if (res.error) {
setError('Invalid input.')
}
else {
// continue
}
};
return (
<Form onSubmit={handleLogin}>
<span>{error}</span>
<input onChangeText={text => setUsername(text)}/>
</Form>
);

Found out how to do it by creating an invisible alert that's visible when I set a variable to the error
{
this.state.status &&
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
{ this.state.status }
</Alert>
}

Related

How do I keep the auth state and prevent re logging in each time i refresh the page or navigate to another page using firebase auth?

I can't seem to figure out how to maintain the login state.
I login, the app shows the condition if logged in, but then if I refresh the page, it asks me to login again.
I am using onAuthStateChanged, I just dont know what else to do.
This shows up when user not logged in
after I click login, this shows up
but when i click refresh, it shows you must login again.
here is my firebase config (the relevant bits)
function useAuth() {
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubsubscribe = onAuthStateChanged(auth, (user) =>
setCurrentUser(user)
);
return unsubsubscribe;
}, []);
return currentUser;
}
export { app, storage, auth, database, useAuth };
I decide to create a function useAuth() inside firebase.config so i dont have to recreate it everywhere i need it.
Here is the login code
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const currentUser = useAuth();
const [loading, setLoading] = useState(false);
function login(email, password) {
return auth
.setPersistence(browserLocalPersistence)
.then(() => {
signInWithEmailAndPassword(auth, email, password).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
console.log(error);
});
})
.catch((error) => {
console.log(error);
});
}
}
And here is the page I want to show if the user is logged in. It shows to correct component if logged in , but when i refresh the page it doesnt remember the logged in user.
here is the code for the page
const Properties = () => {
const currentUser = useAuth();
onAuthStateChanged(auth,(user))
return (
<>
<Head>
<title>Add Property</title>
<meta name="keywords" content="web dev" />
</Head>
<h1>Add Property</h1>
<p>Welcome to the add Property new</p>
{console.log("user logged in? " + currentUser?.email)}
{currentUser ? (
<AddProperty />
) : (
<div>
<p style={{ color: "red" }}>You must be loggedin to add a property</p>{" "}
<Login />
</div>
)}
</>
);
};
export default Properties;

React.js, Auth Component does not redirect properly

I have created this Auth Component and it works fine. Except that, It does not redirect if the unauthenticated user tries to visit /dashboard.
The backend upon receiving /api/me request, knows the user by having the cookie. So I have (Cookie-Session) Authentication technique.
export const UserContext = createContext();
const Auth = ({ children }) => {
const [user, setUser] = useState(null);
const [gotUser, setGotUser] = useState(false);
const navigate = useNavigate();
const getUser = async () => {
const res = await fetch('/api/me');
const data = await res.json();
setUser(data);
if (user) {
setGotUser(true);
}
};
useEffect(() => {
if (!gotUser) {
getUser();
}
}, [user, gotUser, navigate]);
if (!user) {
navigate('/login');
}
console.log(user);
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
};
So the main issue is that no redirection done. Also, The user passed to the context is not updated properly. Maybe because I am confused about what to use in useEffect .
Any help is appreciated.
Issues
There are a couple issues:
The "unauthenticated" state matches the "I don't know yet" state (i.e. the initial state value) and the component is redirecting too early. It should wait until the user state is confirmed.
The navigate function is called as an unintentional side-effect directly in the body of the component. Either move the navigate call into a useEffect hook or render the Navigate component to issue the imperative navigation action.
Solution
Use an undefined initial user state and explicitly check that prior to issuing navigation action or rendering the UserContext.Provider component.
const Auth = ({ children }) => {
const [user, setUser] = useState(); // <-- initially undefined
const navigate = useNavigate();
const getUser = async () => {
try {
const res = await fetch('/api/me');
const data = await res.json();
setUser(data); // <-- ensure defined, i.e. user object or null value
} catch (error) {
// handler error, set error state, etc...
setUser(null); // <-- set to null for no user
}
};
useEffect(() => {
if (user === undefined) {
getUser();
}
}, [user]);
if (user === undefined) {
return null; // <-- or loading indicator, spinner, etc
}
// No either redirect to log user in or render context provider and app
return user
? <Navigate to="/login" replace />
: <UserContext.Provider value={user}>{children}</UserContext.Provider>;
};
useEffect runs after your JSX is rendered, so as your code is made, on a page refresh this if (!user) that calls navigate('/login') will always pass, as before the useEffect does its work, user is null, that inital value you gave to useState. Yet it's not redirecting because navigate does not work inside JSX, it should be replaced with Navigate the component.
Also, in getUser, you have this if (user) juste after setUser(data), that wouldn't work well as user won't get updated immediately, as updating a state is an asynchronous task which takes effect after a re-redner .
To fix your problems you can add a checking state, return some loader while the user is being verified. Also you can optimise a little bit your code overall, like getting ride of that gotUser state:
export const UserContext = createContext();
const Auth = ({ children }) => {
const [user, setUser] = useState(null);
const [checking, setChecking] = useState(true);
const getUser = async () => {
try {
const res = await fetch("/api/me");
const data = await res.json();
setUser(data);
} catch (error) {
setUser(null);
} finally {
setChecking(false);
}
};
useEffect(() => {
if (!user) {
getUser();
}
}, [user]);
if (checking) {
return <p>Checking...</p>;
}
if (!user) {
return <Navigate to="/login" replace />
}
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
};
export default Auth;

Testing async handler and subsequent changes on the page with jest

What i have is the simple page where I have the "Reset Password" button and "Confirm" button by defaut. If you will press "Reset password" it will be fetched from the server and displayed where this button was in "TextInput". Aslo "Confirm" button become active after that.
Here is my component.
export default function App({ profileId, onClose }) {
const [ resetPasswordIsPressed, setResetPasswordIsPressed ] = useState(false);
const [ temporaryPassword, setTemporaryPassword ] = useState(null);
const [ isButtonDisabled, setIsButtonDisabled ] = true;
const handleUserUpdate = () => {
handleChangePassword(false);
onClose()
}
const handleChangePassword = async isPressed => {
const tempPassword = await fetchPassword(isPressed, profileId);
setTemporaryPassword(tempPassword);
setResetPasswordIsPressed(isPressed);
setIsButtonDisabled(false);
}
return (
<div className="App">
{ resetPasswordIsPressed ? (
<TextInput
isDisabled
testId='passwordInput'
value={temporaryPassword}
/>
) : (
<PasswrodResetButton
onClick={() => handleChangePassword(true)}
text='PasswordReset'
/>
)
}
<ConfirmButton isDisabled={isButtonDisabled} onClick={handleUserUpdate}/>
</div>
);
}
and here is fetch function which is imported from separate file
const fetchPassword = async (isPressed, profileId) => {
const getPassword = httpPost(userService, userendPoint);
try {
const {data} = await getPassword({
data: {
profileId
}
});
const { tempPassword } = data;
return tempPassword;
} catch (e) {
console.log(e.message)
}
}
What i need is to test that after clicking "Reset Password" handler was called, after that the "Reset Password" button is not on the page anymore, the "TextInput" with fetched password IS on the page, and the confirm button become active. Here is what I'm trying to do.
describe('User handlers', () => {
it('Should trigger reset password handler', async () => {
const mockCallBack = jest.fn();
const action = shallow(<PasswordResetButton onClick={mockCallBack}/>);
action.simulate('click');
await screen.findByTestId('passwordInput')
})
})
But got 'Unable to find and element by: [data-testid="passwordInput"]'
You're rendering your 'action' separately from your App component, which makes it completely independent.
I can't see in your code what 'screen' is, but most likely it's your shallowly rendered App component, and this is exactly where you need to find your <PasswordResetButton /> and click it.
If you were to add to your <PasswordResetButton /> a testid like you did to <TextInput />, you'd be able to do something like this:
it('Should trigger reset password handler', async () => {
const action = await screen.findByTestId('#passwordResetButton');
action.simulate('click');
const passwordInput = await screen.findByTestId('passwordInput');
expect(passwordInput).toHaveLength(1);
})
But again, I'm not sure what your screen variable is and what exactly screen.findByTestId does and returns.

Dynamically load from API SVG via hooks ReactJs

I have a module that renders an svg. Before it, the module should check on authorization and if ok fetch the file from api via call with a token.
I have the next code
function App() {
const [tableColors, setTableColors] = useState(["gray"]);
const [svg, setSvg] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
const [isErrored, setIsErrored] = useState(false);
new AuthService().getUser().then(user =>{ if(!user) {
Login()
}
else{
useEffect( async () => {
LoadSvg()
.then(res => res.text())
.then(setSvg)
.catch(setIsErrored)
.then(() => setIsLoaded(true))
}, [])
}})
return (
<div className="App">
<header className="App-header">
<SvgLoader svgXML={svg}>
</SvgLoader>
</header>
</div>
);
function Login(){
var a = new AuthService();
a.login();
}
async function LoadSvg(){
return await new ApiService().callApi("https://localhost:44338/api/v1/desk-booking/Plan/getroomplanbyid?roomId=1")
}
}
And the problem I have here is "React Hook "useEffect" cannot be called inside a callback" but without using "useEffect" it fetchs the svg endlessly.
How I can solve the issue?
You are not doing it right, if you do this the "react" way then the solution would look like this
....
function App() {
const [tableColors, setTableColors] = useState(['gray']);
const [svg, setSvg] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);
const [isErrored, setIsErrored] = useState(false);
// state to check if user is loaded, used for svg call
const [userLoaded, setUserLoaded] = useState(false);
// useEffect does not prefer async function
useEffect(() => {
if (!userLoaded) {
new AuthService().getUser().then(user => {
if (!user) {
Login();
// indicate user was loaded
// I would move the login function body here instead since, login is async, so this might not work as intended but you get the idea
setUserLoaded(true);
}
});
}
}, [])
useEffect(() => {
// if userLoaded is true then go ahead with svg loading
if (userLoaded) {
LoadSvg()
.then(res => res.text())
.then(setSvg)
.catch(setIsErrored)
.then(() => setIsLoaded(true));
}
// Add svg useEffect dependency on userLoaded
}, [userLoaded]);
......
Note, this solution is intended to give you an idea of how to do it, It might not work if you copy-paste the code.

Using React Javascript (Form-Onsubmit & calling API not working properly)

i am a bit puzzled with the logic when reading the below code, although the code is working but not exactly as i would like it to behave.
3 queries i have if some one can please clarify.
1- As i understand useEffect is used to invoke the function after render, but in the below code, once the form is sumbitted (onSubmit={credentialVerify}) it will call the credentialVerify() function as below, so i dont think we need useEffect here, but still the code doesnt call the API unless i use the useEffect statement.
2- Also doesnt wait for me to enter my credentails first and as soon as i go to the Signin page it will fetch the API’s (when using useEffect ) and shows the result in the windows, but i try to design in a way that when i click button then it will fetch the API
3- when in the form onsubmit call the credentialVerify function, i have console.log(e) but it is showing as undefined, but as i understand onsubmit will call the function and through the event argument by default.
Below is the snippet of my code.
Any help Appreciated.
import React, { useState, useEffect } from "react";
import "../App.css";
import { Link } from "react-router-dom";
function Signin() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const updateName = (e) => {
setName(e.target.value);
};
const updatePassword = (e) => {
setPassword(e.target.value);
};
const [items, setItems] = useState([]);
useEffect(() => { //Point-1 useEffect- API not call atall without this statement
credentialVerify();
}, []);
const credentialVerify = async (e) => {
console.log(e); //Point-3 this is coming as undefined
const data1 = await fetch("http://localhost:5000/api/customers");
const incomingdata = await data1.json();
console.log(data1);
console.log(incomingdata);
console.log(name, password);
setItems(incomingdata);
};
return (
<div>
<div>
{
<form className="formstyle" onSubmit={credentialVerify}>
<input
type="text"
placeholder="Username"
name="username"
value={name}
onChange={updateName}
/>
<input
type="text"
placeholder="Password"
name="password"
value={password}
onChange={updatePassword}
/>
<button type="submit">Submit</button>
</form>
}
</div>
<div>
{items.map((entry) => {
let key = entry.email;
let valuefirst = entry.firstName;
let valuelast = entry.created_at;
return (
<p key={key}>
{key}: {valuefirst}bb {valuelast}
</p>
);
})}
</div>
</div>
);
}
export default Signin;
For your first question, you are correct - it doesn't make sense to call credentialVerify when your component renders for the first time since that seems to be the handler for when your form gets submitted. Unless you're fetching data prior to displaying your form, you can drop the useEffect hook entirely.
This is also takes care of your second question because the hook will run once when your component renders for the first time, which is indicated by the empty array [] used as a dependency array of the useEffect hook. This is equivalent to componentDidMount in a class-based component, but again, it doesn't make sense to call credentialVerify at this point.
As for your third question, you should probably do something like the following:
const credentialVerify = event => {
event.preventDefault();
(async () => {
const data = await fetch("http://localhost:5000/api/customers")
.then(res => res.json());
.catch(e => e);
console.log(incomingData);
// ...
})();
}
Since you're passing an asynchronous function as your event handler, you might have issues accessing the SyntheticEvent object due to the reasons stated in React docs:
The SyntheticEvent is pooled. This means that the SyntheticEvent object will be reused and all properties will be nullified after the event callback has been invoked. This is for performance reasons. As such, you cannot access the event in an asynchronous way.
reactjs.org/docs/events.html#event-pooling
Your final component should look like the following:
function Signin() {
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const [items, setItems] = useState([]);
const updateName = e => {
setName(e.target.value);
};
const updatePassword = e => {
setPassword(e.target.value);
};
const credentialVerify = event => {
event.preventDefault();
(async () => {
const incomingdata = await fetch("http://localhost:5000/api/customers")
.then(res => res.json())
.catch(e => e);
console.log(incomingdata);
console.log(name, password);
setItems(incomingdata);
})();
};
return (
<div>...</div>
);
}

Categories

Resources