setState hooks in axios post request not working - reactjs

const Login=(props)=>{
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [user, setUser] = useState({});
const login = (e)=>{
e.preventDefault();
axios.post('/login',{email, password})
.then(response => {
setUser(response.data)
console.log(response.data, user, "user data");
}).catch(err => {
console.log(err);
})
}
this is the empty user data i
this is my code for the login request its fetching the data and logging it in the console but it doesnt want to set the user

Because setUser is async. So the new state only update when component rerender. You can check by move console.log outside the login function
console.log(user, "user data");
return(...)

You can use useEffect to check state after component rerender:
useEffect(() => {
console.log(user);
}, [user])

Related

How to fix this error when I use useffect on react: React Hook useEffect has a missing dependency: 'props.history'

I can't really figure whats wrong with my code and I get this error:
" React Hook useEffect has a missing dependency: 'props.history'. Either include it or remove the dependency array "
const mapState = ({ user }) => ({
signInSuccess: user.signInSuccess
});
const SignIn = props => {
const { signInSuccess } = useSelector(mapState);
const dispatch = useDispatch();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
if (signInSuccess){
resetForm()
props.history.push('/')
}
}, [signInSuccess])
const resetForm = () => {
setEmail('');
setPassword('');
}
Illustrating Drew Reese's suggestion. I nixed resetForm in favor of calling your state setters directly in the useEffect hook. These functions can be excluded from the dependency array because React guarantees their consistency.
If you wanted to include the resetForm() function in your effect hook, you should construct as a memoized function with the useCallback() hook to prevent infinite re-renders.
Also, fwiw, if useEffect is routing the user to another page in your app, this component will unmount and you don't need to worry about resetting the email and password states.
const SignIn = (props) => {
const { signInSuccess } = useSelector(mapState);
const dispatch = useDispatch();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
useEffect(() => {
if (signInSuccess) {
setEmail("");
setPassword("");
props.history.push("/");
}
}, [signInSuccess, props.history]);
};

Update global state in axios post request

I have a global token variable that I want to update whenever I make an API request with axios. The problem that I am having is how to update the the token variable since the axios request is not made in a functional component, so I am not able to take advantage of React hooks when making such request.
const logInUser = async (usernameOrEmail, password) => {
//const myContext = useContext(AppContext);
//^ gives an error b/c it's a react hook
axios
.post(
`https://jellybackend.herokuapp.com/authenticate`, {
username: usernameOrEmail,
password: password,
})
.then((response) => {
//myContext.setToken(response.data.token); //update token set , error since not a functional component
console.log(response);
tokenGlobal = response.data.token
})
.catch((error) =>
console.log(error)
);
};
I am making my token a global state, so I have the hook defined in App.js, as seen below
export default function App() {
//define global variable of token
const [token, setToken] = useState("");
const userSettings = {
token: "",
setToken,
};
return (
<AppContext.Provider value={userSettings}>
...
</AppContext.Provider>
);
}
Any idea on how to update my global state variable, or how to refactor my code would be very appreciated!
What I want eventually to happen is that whenever a user logs in, the token is updated, since that is what is returned from the axios post request.
The button below is how a user logs in
function LoginScreen(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const myContext = useContext(AppContext);
return (
...
<Button
onPress={ () => {logInUser(email, password);} //I want to update the token here...
w="40%"
py="4"
style={styles.button}
>
A very simple and trivial refactor would be to pass callback functions to the logInUser utility.
Example:
const logInUser = async (usernameOrEmail, password, onSuccess, onFailure) => {
axios
.post(
`https://jellybackend.herokuapp.com/authenticate`, {
username: usernameOrEmail,
password: password,
})
.then((response) => {
onSuccess(response);
console.log(response);
})
.catch((error) =>
onFailure(error);
console.log(error);
);
};
...
function LoginScreen(props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const myContext = useContext(AppContext);
const successHandler = response => myContext.setToken(response.data.token);
const failureHandler = error => myContext.setToken(null);
return (
...
<Button
onPress={() => logInUser(email, password, successHandler, failureHandler)}
w="40%"
py="4"
style={styles.button}
>
...
</Button>
...
);
}
You could setup your axios call in a module that can then return the value that you would like to store in global state.
Your axios call doesn't have to exist within a functional component per se, but it would need to be imported/called within one for this solution.
So, you could change your axios call to be within a module function that could then be exported, say globalsapi.js, then imported to your functional component:
exports.logInUser = async () => {
const globalData = await axios
.post(
`https://jellybackend.herokuapp.com/authenticate`, {
username: usernameOrEmail,
password: password,
});
const token = await globalData.data.token;
return token;
}
Now, wherever you decide to call the setToken state update, you can just import the function and set the global token:
import { logInUser } from './globalsapi';
logInUser().then(data => {
myContext.setToken(data);
});
You could pass whatever parameters needed to the logInUser function.

Infinite Re-render even with the use of useEffect Hook

I am trying to create an authorized user checker with React, however, my component keeps on re-rendering.
Here's my approach:
I created an authBreaker state so that my useEffect will update every time it changes and not the auth state which is being updated inside the useEffect.
However, it still keeps on re-rendering even though authBreaker is not updated.
I also, tried removing the dependencies of my useEffect using [] But it resulted to the same behavior.
const Login = React.memo(() => {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [authBreaker, setAuthBreaker] = useState(false)
const [auth, setAuth] = useState(false)
const isAuthorized = () => {
const user = Cookies.get('token')
user && setAuth(true)
console.log(auth)
}
const login = async () => {
try {
//server code here...
//if login successful..
setAuthBreaker(true)
} catch (e) {
console.log(e)
}
}
useEffect(() => {
isAuthorized()
}, [authBreaker])
return (
<div className="pml-form">
{ auth && <Redirect to="/"/> }
//code here...
</div>
)
})
Any idea why it keeps on re-rendering? Thanks!

once the data has been fetched the state value are not getting updated

I'm trying to change the state value once the data has been fetched. I can see that the JSON has been fetched on the network tab but the state value hasn't been changed. State values are logged before the fetch request, I've added await but it hasn't been resolved yet. Do I've to use useEffect for a fetch request, I've tried to use useEffect but it triggers the request once I import this hook is there a workaround?
import axios from 'axios'
import { useState } from 'react'
export const useSignup = () => {
const [loading, setLoading] = useState(true)
const [status, setStatus] = useState(false)
const [msg, setMsg] = useState('')
const registerUser = async (emailAddress, password) => {
try {
await axios
.post('/signup', {
emailAddress: emailAddress,
password: password,
})
.then((res) => {
setStatus(res?.data.success)
setMsg(res?.data.msg)
})
.catch((err) => {
setStatus(err?.response.data.success)
setMsg(err?.response.data.msg)
})
} catch (err) {
console.error(err)
setStatus(false)
setMsg('Error Occured')
} finally {
console.log(msg, status)
setLoading(false)
}
}
return { loading, status, msg, registerUser }
}
You should trigger your function call via a useEffect hook.
Also, if you are using async/await you shouldn't mix it with a Promise-based approach.
Modify the custom hook to accept the two parameters, add the useEffect call and edit your registerUser function:
export const useSignup = (emailAddress, password) => {
const [loading, setLoading] = useState(true);
const [status, setStatus] = useState(false);
const [msg, setMsg] = useState('');
const registerUser = async (emailAddress, password) => {
try {
const { data } = await axios.post('/signup', { emailAddress, password })
setStatus(data.success)
setMsg(data.msg)
} catch (err) {
console.error(err);
setStatus(false);
setMsg('Error Occured');
}
};
useEffect(() => {
registerUser(emailAddress, password);
}, [])
return { loading, status, msg, registerUser };
};
Then you can call your useSignup hook like this
const { loading, status, msg, registerUser } = useSignup('username', 'password')

How to chain useLazyQuery hook in handler function

I want to chain apollo useMutation and useQuery hooks in a handler function. I cannot seem to get the data property of the useLazyQuery hook to alias - or for that matter the mutation properties loading, error, data. I could get the same effect with useMemo, but I want to explore what I can do with the lazy query in a promise chain.
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [requestLogin, { loading, error }] = useMutation(LOGIN_USER)
const [getUserProfile, {
loading: loadingProfile,
error: errorProfile,
data: profileData
}] = useLazyQuery(GET_USER_PROFILE)
const submitHandler = (event) => {
event.preventDefault()
requestLogin({
variables: {
email: email,
password: password
}
}).then(({ data }) => {
console.log("INITIAL DATA", data)
login(data.login.token, data.login.user)
}).then(({ data, profileData }) => getUserProfile({
variables: {
account_id: data.login.user.account.id
}
})).then(profileData => {
console.log("DATA", profileData)
history.push('/')
})
}

Resources