I want to passing data between components but I have a problem. Not getting any error as long as I don't passing data yet, it's fine. When I try to show the props in the console, I can easily see what I want (history,match,location,AuthStore). But when I try to pass the data, I can only see value and AuthStore in the console and value returns empty. Where am I wrong?
front.layout.js
import Profile from '../../Views/profile'
const Layout = (props) => {
const [user, setUser] = useState({});
const [isLoggedIn, setIsLoggedIn] = useState(false);
props.AuthStore.getToken();
const history = useHistory();
useEffect(() => {
const token =
props.AuthStore.appState != null
? props.AuthStore.appState.user.access_token
: null;
axios
.post(
"/api/authenticate",
{},
{
headers: {
Authorization: "Bearer " + token,
},
}
)
.then((res) => {
if (!res.data.isLoggedIn) {
history.push("/login");
}
setUser(res.data.user);
setIsLoggedIn(res.data.isLoggedIn);
})
.catch((error) => {
history.push("/login");
});
}, []);
return (
<>
<Profile value={user} />
</>
)
index.js
const Profile = (props) => {
console.log(props);
const { params } = props.match;
const [data, setData] = useState({});
const history = useHistory();
if(props.location.key){
useEffect(() => {
axios
.get(
`/api/${params.username}`,
{
headers: {
Authorization:
"Bearer " +
props.AuthStore.appState.user.access_token,
},
}
)
.then((res) => {
if (res.data.username) {
setData(res.data);
}
})
.catch((error) => {
console.log(error);
});
}, []);
}
Related
I'm using an axios call to a database to get "about me" data, for client to update. DB is connected properly, as I am able to log in just fine, I've isolated this issue pretty well to my GET request.
My context provider file:
import React, { useState } from 'react'
import axios from 'axios'
export const UserContext = React.createContext()
const userAxios = axios.create()
userAxios.interceptors.request.use((config) => {
const token = localStorage.getItem("token")
config.headers.Authorization = `Bearer ${token}`
return config
})
const UserProvider = (props) => {
const initState = {
user: JSON.parse(localStorage.getItem("user")) || {},
token: localStorage.getItem("token") || "",
authErrMsg: ""
}
const [userState, setUserState] = useState(initState)
const [dataState, setDataState] = useState({
bioData: []
})
const login = credentials => {
axios.post("/auth/login", credentials)
.then(res => {
const { user, token } = res.data
localStorage.setItem("user", JSON.stringify(user))
localStorage.setItem("token", token)
setUserState(res.data)
})
.catch(err => handleAuthErr(err.response.data.errMsg))
}
const handleAuthErr = errMsg => {
setUserState(prevUserState => ({
...prevUserState,
authErrMsg: errMsg
}))
}
const logout = () => {
localStorage.removeItem("token")
localStorage.removeItem("user")
setUserState({
user: {},
token: "",
authErrMsg: ""
})
}
const getData = () => {
axios.get('/info/bio')
.then(res => {
setDataState(prevData => ({
...prevData,
bioData: res.data
}))
})
.catch(err => {
console.log(err)
})
}
const deleteBio = (id) => {
userAxios.delete(`/api/bio/${id}`)
.then(res => {
setDataState(prevData => ({
...prevData,
bioData: dataState.bioData.filter(bio => bio._id !== id)
}))
})
.catch(err => console.log(err.response.data.errMsg))
}
const addBio = (newText) => {
const newBio = {
bioText: newText
}
userAxios.post('/api/bio', newBio)
.then(res => {
getData()
})
.catch(err => console.log(err))
}
const editBio = (update, id) => {
const updatedBio = {
bioText: update
}
userAxios.put(`/api/bio/${id}`, updatedBio)
.then(res => {
console.log(res.data, 'edited')
getData()
})
.catch(err => console.log(err))
}
return (
<UserContext.Provider
value={{
user: userState.user,
token: userState.token,
authErrMsg: userState.authErrMsg,
login: login,
logout: logout,
getData: getData,
dataState: dataState,
editBio: editBio,
deleteBio: deleteBio,
addBio: addBio
}}>
{props.children}
</UserContext.Provider>
)
}
export default UserProvider
Here's my Bio component. The loading effect never changes because for some reason, no "bioData" is saving, in the provider. Tested it with that little button/handleClick and coming up an empty array.
import React, {useContext, useState, useEffect} from 'react'
import { UserContext } from './context/userProvider'
const Bio = () => {
const { token, editBio, dataState: {bioData} } = useContext(UserContext)
const [loader, setLoader] = useState('Loading')
useEffect(() => {
if(bioData[0]?._id === undefined){
setLoader('Loading')
}else {
setLoader(bioData[0]?._id)
}
})
// let initText = bioData[0].bioText
const [bioText, setBioText] = useState("initText")
const handleChange = (e) => {
setBioText(e.target.value)
}
const handleUpdate = () => {
editBio(bioText, bioData[0]._id)
alert`Bio successfully updated. :)`
}
const handleClick = () => {
console.log(bioData)
}
return (
<div className='bio'>
<h1>About Me</h1>
<div className='bio-content'>
{loader === 'Loading' ?
<div>
<p>Loading...</p>
<button onClick={handleClick}>thing</button>
</div>
:
<>
{token ?
<div className="editBio">
<p>edit mee</p>
</div>
:
<h4>{bioData[0].bioText}</h4> }
</>
}
</div>
</div>
)
}
export default Bio
Thanks in advance guys! Let me know if I can post routes or anything that might be helpful.
Relatively new to using hooks, but I create this useFetch hook and I am trying to sign in a user after getting their information from text fields, I am able to create a successful request by hard coding {email:someemail, password: somepassword} but the issue arises when I am trying to grab the input from my state.
The error states I am missing a parameter
Here is my component:
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// #ts-ignore
const postData =useData({email:email, password:password})
const thisWorks = useData({email:"someEmail", password:"somePassword"})
return (
<>
{console.log(thisWorks)}
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={() => postData}
text="Login"
/>
</>
)
}
And here is my query:
const useData = (data: loginInfo): QueryType[] | string => {
const fetched = useFetch<Query>(
"example.com",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
);
if (fetched.status === "loading") {
return fetched.status;
} else if (fetched.status === "loaded") {
const { payload } = fetched;
// #ts-ignore
return payload;
} else if (fetched.status === "error") {
return fetched.status;
}
return "Error";
};
my useFetch Hook:
const useFetch = <T>(url: string, headers?: Header | any) => {
const isCurrent = useRef(true);
const [result, setResult] = useState<Service<T>>({
status: "loading",
});
useEffect(
() => () => {
// called when the component is going to unmount
isCurrent.current = false;
},
[]
);
useEffect(() => {
fetch(url, headers)
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return result;
};
I'm curious to ask,
will you send many API requests while you typing email and password?
<Button
onClick={() => postData}
text="Login"
/>
this code doesn't seem to work, postData is not a function, just an object
may be you can modify your code like this (I'm not tested just sample code)
your component
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const login = useFetch('http://path/to/login', { method: 'POST' })
React.useEffect(() => {
if (login.status === 'loaded') {
// do your stuff
}
}, [login.status])
const handleLogin = () => {
login.setPayload({ email, password })
}
if (login.status === 'loading') {
return <div>Loading...</div>
}
if (login.status === 'error') {
return <div>{login.error}</div>
}
return (
<>
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={handleLogin}
text="Login"
/>
</>
)
}
useFetch
const useFetch = <T>(url: string, options: RequestInit) => {
const [payload, setPayload] = React.useState({})
const [start, setStart] = React.useEffect(false)
useEffect(() => {
if (!start) {
return
}
fetch(url, {
...options,
headers: {
"Content-Type": "application/json",
...options.headers
},
body: JSON.stringify(payload),
})
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }))
.finally(() => setStart(false))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, start, payload, options]);
return {
...result,
setPayload: (payload) => {
setPayload(payload)
setStart(true)
}
}
}
This can be written using the useEffect hook
const useFetch = (url, options) => {
const [response, setResponse] = React.useState(null);
const [status, setStatus] = React.useState("loading");
useEffect(async () => {
try {
const res = await fetch(url, options);
const json = await res.json();
setResponse(json);
setStatus("success");
} catch (error) {
setStatus("error");
}
});
return [status, response];
};
I'm using react useParams hooks for get url params id. In component AppMessageItems i get id, but how i can send this data to getMessages function for send to endpoint. But when i try get params id in the getMessages function react give error about invalid using hooks
const AppMessageItems = ( {messages, getMessages} ) => {
let { id } = useParams();
console.log(id) // 123
useEffect( () => {
const setUserMessageDataToLocalStorage = localStorage.getItem('token');
getMessages(setUserMessageDataToLocalStorage)
}, []);
return (
<Container>
<TabPanelContainer>
<TabPanelHeader/>
<TabPanelBody />
<TabPanelFooter/>
</TabPanelContainer>
</Container>
)
};
const getMessages = (token) => async (dispatch) => {
// test data
const all = {
pass: 'Hello World',
};
try {
const getMessages = await axios.post(API.getUserMessages, {}, {headers: {"Authorization": 'Bearer ' + token}})
.then(res => res.data);
dispatch(getMessagesDispatch(all));
} catch (err) {
console.log("Error GET_MESSAGES");
}
};
As I commented. here's the walkthrough:
const AppMessageItems = ( {messages, getMessages} ) => {
let { id } = useParams();
const dispatch = useDispatch();
console.log(id) // 123
useEffect( () => {
const setUserMessageDataToLocalStorage = localStorage.getItem('token');
dispatch(getMessages(setUserMessageDataToLocalStorage, id))
}, [id, dispatch]);
return (
<Container>
<TabPanelContainer>
<TabPanelHeader/>
<TabPanelBody />
<TabPanelFooter/>
</TabPanelContainer>
</Container>
)
};
const getMessages = (token, id) => async (dispatch) => {
// use the id param whereever
// test data
const all = {
pass: 'Hello World',
};
try {
const {data} = await axios.post(API.getUserMessages, {}, {headers: {"Authorization": 'Bearer ' + token}})
dispatch(getMessagesDispatch(all));
} catch (err) {
console.log("Error GET_MESSAGES");
}
};
i am a newbie to react but i'm learning and need your help here.
I use Auth0 for Authentication and i have implemented their react sample in parts:
https://auth0.com/docs/quickstart/spa/react/01-login
This are parts of my code:
App.js:
<Auth0Provider
domain={AUTH_CONFIG.domain}
client_id={AUTH_CONFIG.clientId}
redirect_uri={AUTH_CONFIG.callbackUrl}
onRedirectCallback={onRedirectCallback}
>
<Router history={history}>
<RequireAuthentication>
<MyTheme>
<MyLayout />
</MyTheme>
</RequireAuthentication>
</Router>
</Auth0Provider>
Auth0Provider:
import React, { useState, useEffect, useContext } from "react";
import createAuth0Client from "#auth0/auth0-spa-js";
import jwtDecode from "jwt-decode";
import axios from "axios";
import AUTH_CONFIG from "./auth0Config";
import { useDispatch } from "react-redux";
import * as authActions from "app/auth/store/actions";
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popupOpen, setPopupOpen] = useState(false);
const dispatch = useDispatch();
useEffect(() => {
const initAuth0 = async () => {
console.log("initAuth0 start");
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
const isAuthenticated = await auth0FromHook.isAuthenticated();
console.log("Authenticated from init: " + isAuthenticated);
setIsAuthenticated(isAuthenticated);
setLoading(false);
console.log("initAuth0 end");
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopup = async (params = {}) => {
setPopupOpen(true);
try {
await auth0Client.loginWithPopup(params);
} catch (error) {
console.error(error);
} finally {
setPopupOpen(false);
}
const user = await getUserData();
setUser(user);
dispatch(authActions.setUserDataAuth0(user));
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
if (!auth0Client) {
console.warn("Auth0 Service didn't initialize, check your configuration");
return;
}
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = await getUserData();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
dispatch(authActions.setUserDataAuth0(user));
};
const getAccessToken = async () => {
const accessToken = await auth0Client.getTokenSilently({
audience: AUTH_CONFIG.identity_audience,
scope: "read:allUsers read:UserPermission"
});
return accessToken;
};
const getIdToken = async () => {
if (!auth0Client) {
console.warn("Auth0 Service didn't initialize, check your configuration");
return;
}
const claims = await auth0Client.getIdTokenClaims();
return claims.__raw;
};
const getTokenData = async () => {
const token = await getIdToken();
const decoded = jwtDecode(token);
if (!decoded) {
return null;
}
return decoded;
};
const getUserData = async () => {
console.log("getuserdata");
const tokenData = await getTokenData();
const accessToken = await getAccessToken();
return new Promise((resolve, reject) => {
const { sub: userId } = tokenData;
const UserService =
"https://localhost:44312/api/v1/usermanagement/user/" + userId;
axios
.get(UserService, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": "GET,HEAD,OPTIONS,POST,PUT",
"Access-Control-Allow-Headers":
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers",
"Content-Type": "application/json",
Authorization: "Bearer " + accessToken
}
})
.then(response => {
resolve(response.data);
})
.catch(error => {
// handle error
console.warn("Cannot retrieve user data", error);
reject(error);
});
});
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popupOpen,
loginWithPopup,
handleRedirectCallback,
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getTokenWithPopup: (...p) => auth0Client.getTokenWithPopup(...p),
logout: (...p) => auth0Client.logout(...p)
}}
>
{children}
</Auth0Context.Provider>
);
};
RequireAuthentication:
import React, { useEffect } from "react";
import { useAuth0 } from "app/auth/AuthProvider";
import { SplashScreen } from "#my";
import history from "#history";
export const RequireAuthentication = ({ children }) => {
const { isAuthenticated, loading } = useAuth0();
useEffect(() => {
console.log("checkAuth");
if (!loading) checkAuth();
// eslint-disable-next-line
}, []);
const checkAuth = () => {
console.log("checkAuth isAuthenticated: " + isAuthenticated);
console.log("checkAuth loading: " + loading);
if (!isAuthenticated && !loading) {
history.push("/login");
}
};
return isAuthenticated ? (
<React.Fragment>{children}</React.Fragment>
) : (
<SplashScreen />
);
};
callback.js:
import React, { useEffect } from "react";
import { SplashScreen } from "#my";
import { useAuth0 } from "app/auth/AuthProvider";
function Callback(props) {
const { isAuthenticated, handleRedirectCallback, loading } = useAuth0();
useEffect(() => {
const fn = async () => {
if (!loading) {
console.log("handleRedirectCallback: " + loading);
await handleRedirectCallback();
}
};
fn();
}, [isAuthenticated, loading, handleRedirectCallback]);
return <SplashScreen />;
}
export default Callback;
The problem is that the RequireAuthentication Component is rendered before the Auth0Provider is completely initialized and therefore i get never the isAuthenticated on "true".
The RequireAuthentication Component is a child of the Auth0Provider. Is it possible to wait for the Auth0Provider is fully initialized before rendering the RequireAuthentication Component???
What is the right way here?? Am I completely wrong?
Thanks
Chris
Depend on loading and isAuthenticated items in useEffect so that component will re render once they change.
useEffect(() => {
console.log("checkAuth");
if (!loading) checkAuth();
// eslint-disable-next-line
}, [loading, isAuthenticated]);
I keep encountering an infinity loop when trying to use the useEffect hook to fetch and set data. ive tried 3 variations of the hook and they all produce a loop, how do i stop this from happening?
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
})
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[])
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},[profile.posts])
EDIT: Here is the PostApi.getPostsByUser code
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
})
.then(response => {
console.log("Posts by User");
console.log(response.data);
return response.data;
})
.catch(err => console.log(err))
)
}
EDIT: Function component code:
const Posts = () => {
const [{auth}] = useAuth();
const [{profile},, setPosts] = useProfile()
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response));
},[]);
console.log(profile)
return(
<div className="User-Post">
<div className="New-Post">
<NewPost />
</div>
<div className="User-Posts-Content">
{
profile.posts ? profile.posts.map((item, key) => {
return <Post post={item} key={key} />
}) : null
}
</div>
</div>
)
}
export default Posts
Change:
const [auth] = useAuth();
const [profile, setPosts] = useState();
const setPosts = posts => { setPosts(state => ({ ...state, profile: {
...state.profile, posts: posts } })) }
getPostsByUser: (userId, token) => {
return(
axios
.get("/api/posts/by/" + userId, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Bearer " + token
}
});
}
and
useEffect(() => {
PostApi.getPostsByUser(auth.user._id, auth.token)
.then(response => setPosts(response.data));
},[]);
You can try like this.
useEffect(() => {
const get = async () => {
const response = await PostApi.getPostsByUser(auth.user._id, auth.token);
setPosts(response);
}
get();
},[]);
This works for me ... and the simplest solution too
const [toggle, setToggle] = useState(false);
useEffect(() => {
(async () => {
PostApi.getPostsByUser(auth.user._id, auth.token).then(response => setPosts(response))
})()
},toggle)