I have an axios request that allows me to get user information if he's logged and display it on his profile page.
My question is where to call it to avoid too many request and enhance optimization.
I've seen people calling it on top in App.js but I find it weird. I'm currently calling it in my container component that render UserProfile or LogForm depending if user is logged, but then a request is made each time user clicks on his profile page and I don't feel that it is the right way.
Here is my code:
Profile.tsx
const Profile = () => {
const dispatch = useDispatch();
const userId = useSelector((kinaccess: RootStateOrAny) => kinaccess.formsReducer.signInForm.success.userId);
useEffect(() => {
dispatch(Action.getUserInfo(userId));
}, [userId, dispatch]);
return <div>{userId ? <UserProfile /> : <LogForm />}</div>;
};
UserProfile.tsx
const UserProfile = () => {
const userInfo = useSelector((kinnacess: RootStateOrAny) => kinnacess.userReducer.user.success);
if (!userInfo) return null;
const { name, firstName, email } = userInfo;
return (
<section className='user-profile'>
<h3>
Hello {firstName} {name}
</h3>
<p>Is your mail adress still {email}</p>
</section>
);
};
An my Routes.tsx
const Routes = () => {
return (
<Routing>
<Route index element={<Home />} />
<Route path='contact' element={<ContactForm />} />
<Route path='profile' element={<Profile />} />
<Route path='*' element={<Error404 />} />
</Routing>
);
};
Related
I'm currently working on login functionality, and I notice on refresh it goes back to the login page. I was wondering if there was a workaround on this sample code.
I do know that the reason why is because on every render the loginStatus runs and it sets it to false, but is there a work around that ?
I am storing it into local storage as well
function app () {
const [loginStatus, setloginStatus] = useState(false)
const loginButtonSubmit ( event) => {
if (email field & password field is authenticatied){
setloginStatus(true)
navigate("/InfoPage")
}
}
useEffect(() => {
const data = window.localStorage.getItem("Test")
setloginStatus(JSON.parse(data))
}
useEffect(() => {
window.localStorage.setItem("Test", JSON.stringify(loginStatus))
}, [loginStatus])
const loginForm = () => {
<h1> login page functionality</h1>
<div onClick=(loginButtonSubmit) </div>
}
return (
<div className="app">
<Routes>
<Route path="/" element {loginForm()}
<Route exact path="/Money" element={loginStatus ? <MoneyPage /> : loginForm()} />
</Routes>
</div>
)
}
For my first project with React, i tried to use useContext() for call my data mocked.
But after that, i have a problem.
The problem :
You go to a housing page, you change the apartment ID in the URL with a Dummy ID and you update it.
Great! The redirection to the 404 is done.
So you go back to the home page by clicking on the link of the 404.
You go back to any housing page => the 404 is always present.
You repeat, you then return to the home page and you click again on a housing card and WOW the problem no longer exists.
My Provider
import React, { useState, createContext } from "react"
import fetchLocationData from "../../services/localFetch"
export const FetchDataContext = createContext()
export const FetchDataProvider = ({ children }) => {
const [locationData, setLocationData] = useState({})
const [locationsData, setLocationsData] = useState([])
const [allLocationLoading, setAllLocationLoading] = useState(false)
const [isLocationLoading, setIsLocationLoading] = useState(false)
const [errorAPI, setErrorAPI] = useState(false)
const [error404, setError404] = useState(false)
async function fetchLocationById(locId) {
try {
setError404(false)
const response = await fetchLocationData.getLocById(locId)
response ? setLocationData(response) : setError404(true)
} catch (err) {
console.log(err)
setErrorAPI(true)
} finally {
setIsLocationLoading(true)
console.log("in provider :", error404)
}
}
async function fetchAllLocations() {
try {
const response = await fetchLocationData.getAll()
setLocationsData(response)
} catch (err) {
console.log(err)
setErrorAPI(true)
} finally {
setAllLocationLoading(true)
}
}
return (
<FetchDataContext.Provider
value={{
errorAPI,
error404,
isLocationLoading,
allLocationLoading,
locationData,
locationsData,
fetchLocationById,
fetchAllLocations,
}}
>
{children}
</FetchDataContext.Provider>
)
}
My router
function App() {
return (
<>
<GlobalStyle />
<BlocPage>
<Header />
<FetchDataProvider>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/apartment/:locId" element={<ProfileLocation />} />
<Route path="*" element={<Error />} />
</Routes>
</FetchDataProvider>
</BlocPage>
<Footer />
</>
)
}
My housing page
function ProfileLocation() {
const { locId } = useParams()
const {
errorAPI,
error404,
locationData,
isLocationLoading,
fetchLocationById,
} = useContext(FetchDataContext)
useEffect(() => {
fetchLocationById(locId)
}, [])
const rating = parseInt(locationData.rating)
if (errorAPI) {
return (
<span>
Oups une erreur est survenue ... Veuillez recommencer ultérieurement.
</span>
)
}
if (error404) {
return <Navigate to="/error404" />
}
return (
<div>
{isLocationLoading ? (
<>
<Slider pictures={locationData.pictures} />
<ResponsiveWrapper>
<FirstSectionWrapper>
<Title>{locationData.title}</Title>
<Geolocation>{locationData.location}</Geolocation>
<TagsWrapper>
{locationData.tags.map((tag, index) => (
// eslint-disable-next-line react/no-array-index-key
<Tag key={`${tag}-${index}`} label={tag} />
))}
</TagsWrapper>
</FirstSectionWrapper>
<SecondSectionWrapper>
<OwnerWrapper>
<HomeOwnerName>{locationData.host.name}</HomeOwnerName>
<HomeOwerPicture
src={locationData.host.picture}
alt={locationData.host.name}
/>
</OwnerWrapper>
<StarsWrapper>
<StarScale ratingValue={rating} starType="full" />
<StarScale ratingValue={rating} starType="empty" />
</StarsWrapper>
</SecondSectionWrapper>
</ResponsiveWrapper>
<CollapseSectionWrapper>
<CollapseWrapper>
<Collapse
pageType="profil"
label="Description"
contentType="paragraph"
contentText={locationData.description}
/>
</CollapseWrapper>
<CollapseWrapper>
<Collapse
pageType="profil"
label="Équipements"
contentType="list"
contentText={locationData.equipments}
/>
</CollapseWrapper>
</CollapseSectionWrapper>
</>
) : (
<Loader />
)}
</div>
)
}
export default ProfileLocation
Maybe it’s an async code problem?
The issue is that only navigating forward to a route rendering a component that calls fetchLocationById will ever manage to clear/set the error404 state back to false.
I suggest instead of passing error404 back to the components that the FetchDataProvider handles rendering the error UI upon the error condition. Having it redirect to a specific error page.
Example:
<FetchDataProvider>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/apartment/:locId" element={<ProfileLocation />} />
<Route path="/404" element={<Error404 />} /> // <-- add route
<Route path="*" element={<Error />} />
</Routes>
</FetchDataProvider>
...
const Error404 = () => (
<div>Oups une erreur est survenue ... Veuillez recommencer ultérieurement.</div>
);
...
import { useNavigate } from 'react-router-dom';
export const FetchDataProvider = ({ children }) => {
const navigate = useNavigate();
...
async function fetchLocationById(locId) {
try {
setError404(false)
const response = await fetchLocationData.getLocById(locId)
if (response) {
setLocationData(response);
} else {
navigate("/error404", { replace: true });
}
} catch (err) {
navigate("/error404", { replace: true });
} finally {
setIsLocationLoading(true);
}
}
...
return (
...
);
}
...
function ProfileLocation() {
const { locId } = useParams();
const {
locationData,
isLocationLoading,
fetchLocationById,
} = useContext(FetchDataContext);
useEffect(() => {
fetchLocationById(locId);
}, [fetchLocationById, locId]); // <-- don't forget to add proper dependencies
const rating = parseInt(locationData.rating);
return (
<div>
{isLocationLoading ? (
<>
<Slider pictures={locationData.pictures} />
<ResponsiveWrapper>
<FirstSectionWrapper>
<Title>{locationData.title}</Title>
<Geolocation>{locationData.location}</Geolocation>
<TagsWrapper>
{locationData.tags.map((tag, index) => (
// eslint-disable-next-line react/no-array-index-key
<Tag key={`${tag}-${index}`} label={tag} />
))}
</TagsWrapper>
</FirstSectionWrapper>
<SecondSectionWrapper>
<OwnerWrapper>
<HomeOwnerName>{locationData.host.name}</HomeOwnerName>
<HomeOwerPicture
src={locationData.host.picture}
alt={locationData.host.name}
/>
</OwnerWrapper>
<StarsWrapper>
<StarScale ratingValue={rating} starType="full" />
<StarScale ratingValue={rating} starType="empty" />
</StarsWrapper>
</SecondSectionWrapper>
</ResponsiveWrapper>
<CollapseSectionWrapper>
<CollapseWrapper>
<Collapse
pageType="profil"
label="Description"
contentType="paragraph"
contentText={locationData.description}
/>
</CollapseWrapper>
<CollapseWrapper>
<Collapse
pageType="profil"
label="Équipements"
contentType="list"
contentText={locationData.equipments}
/>
</CollapseWrapper>
</CollapseSectionWrapper>
</>
) : (
<Loader />
)}
</div>
);
}
I have a timeout dialog that I want to render in various components when a token expires at X minutes (I have hardcoded the times for now). Material-UI Dialog is being used for the dialog pop up. I have two timeouts set, one at 29min:30sec for a warning message and 30min will force the logout.
I have looked at the following example for ideas.
const AlertDialog = props => {
const [warningTimeout, setWarningTimeout] = useState(1770000);
const [limitTimeout, setLimitTimeout] = useState(1800000);
const [open, setOpen] = useState(true);
const handleContinue = () => {
//this will make an api call to refresh the token, where we must update the context with the new token
setOpen(false);
};
const handleLogout = () => {
//this will destroy the session, clear localstorage
};
const warn = () => {
console.log("Warning, 30 minutes has past");
};
const logout = () => {
console.log("You have been logged out");
};
const setTimeouts = () => {
const warnTimeout = setTimeout(warn, warningTimeout);
const logoutTimeout = setTimeout(logout, limitTimeout);
};
useEffect(() => {
setTimeouts();
});
return (
<div>
<Dialog
disableBackdropClick
disableEscapeKeyDown
open={open}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Session Timeout"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
If you wish to continue the current session please press continue.
You have 30 seconds to decide or you will be logged out.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleLogout} color="primary">
Logout
</Button>
<Button onClick={handleContinue} color="primary" autoFocus>
Continue
</Button>
</DialogActions>
</Dialog>
</div>
);
};
export default AlertDialog;
The project has protected routes so they look like this. I want to pass this alert dialog to all the <AdminPrivateRoute>'s
const Routes = () => (
<UserProvider>
<TokenProvider>
<Router>
<Switch>
<Route exact path="/" component={LoginForm} />
<AdminPrivateRoute path="/dashboard" component={Dashboard} />
<AdminPrivateRoute path="/secondary-page" component={SecondaryPage} />
</Switch>
</Router>
</TokenProvider>
</UserProvider>
);
Is wrapping the above components with alert dialog a viable option?
<AdminPrivateRoute path="/dashbaord" component={AlertDialog(Dashboard)} />
Is there a better approach?
Codesandbox for reference, you can use any credentials to log in.
Placing the <AlertDialog /> component outside the <Switch /> seems to do the trick.
const Routes = () => (
<Router>
<AlertDialog />
<Switch>
<Route exact path="/" component={LoginForm} />
<AdminPrivateRoute path="/dashboard" component={Dashboard} />
</Switch>
</Router>
);
I am trying to use Reach Router to navigate to a result url after a form submit. Using props.navigate takes be to the desired url, but no component is rendered (I am trying to render <Result/> after the query.
To add to the complexity, I would like to pass the personsResult prop to Result
Here is an outline of my current approach.
const App = () => {
return (
<div>
<Router>
<Search path="/" />
<List path="list" />
<Result path="result/:query" /> //!
</Router>
</div>
)
}
const Search = props => {
const [query, setQuery] = useState("")
const [personsResult, setPersonsResult] = useState("") //Pass to result
const handleSubmit = async e => {
e.preventDefault()
const person = await personsService.get(query)
setPersonsResult(person)
props.navigate(`result/${query}`) //!
}
return (
<div>
...
<SearchForm
handleSubmit={handleSubmit}
query={query}
setQuery={setQuery}
/>
</div>
)
}
I am open to switching to react-router-dom if there is a simpler means of solving this.
Im having problems at the result component i can't render the filteredData, at result this.props.dataToRender ain't working thats the problem.
I have all the logic already, but when trying to display the result of the search on another component i'm getting no information.
If i execute the code on the app component i get the result of the search but if i try to pass it as props i don't get a result at the other side.
state = { data: [], filteredData: [], value: "" };
async componentDidMount() {
axios
.get("https://tenjoucesar.github.io/data.json")
.then(response => response.data)
.then(data => {
this.setState({ data });
console.log(this.state.data);
});
}
handleSubmit(e) {
const { value, data } = this.state;
e.preventDefault();
let email = value;
const filtered = data.filter(e => e.email === email);
this.setState({ filteredData: filtered });
}
handleChange(e) {
this.setState({ value: e.target.value });
}
InfomData = person => {
return (
<div className="result" key={person.email}>
<h3 >
{person.name}, {person.age}
</h3>
<p className="paragraph--result">{person.notes}</p>
</div>
);
};
render() {
const { filteredData } = this.state;
const dataToRender = filteredData.map(e => this.InfomData(e));
return (
<React.Fragment>
<Nav />
<Switch>
<>
<Route exact path="/" component={Header} />
<Search
component={Search}
handleSubmit={this.handleSubmit.bind(this)}
handleChange={this.handleChange.bind(this)}
value={this.props.value}
type={this.props.email}
/>
<Route exact path="/" component={Reverse} />
<Route
path="/result"
component={Result}
dataToRender={dataToRender}
/>
</>
</Switch>
<Footer />
</React.Fragment>
);
}
}
Here at Result component i tried moving informData and the const dataToRender but didn't work.
class Result extends Component {
render() {
return (
<div className="section-result">
<div className="container--result">
<h1 className="heading-primary--result">Results</h1>
<p className="paragraph--results">
Look at the result below to see the details of the person you're
searched for.
</p>
</div>
{this.props.dataToRender}
<TryAgain />
</div>
);
}
}
I just want to get the result on the result component, i had tried with all that i found, nothing worked.
Could someone tell me what do i have to do ? I'm sorry still learning about react
Just Modify the Route to this
<Route
path="/result"
render={() => <Result dataToRender={dataToRender} />}
/>
Since you have to inject props to a component, this is the way
Pass Route's render an arrow function with component and customProps
EDIT
If you also need the history, location, search props that are passed by default when we use <Route path="/" component={Something} /> then pass them explicitly
<Route
path="/result"
render = {(props) => <Result dataToRender={dataToRender} {...props} /> }
/>