Traversing to another page in reactjs inside an onclick button event - reactjs

I have a simple reactjs button (Material-UI components) and I need to trigger its onclick event, inside that I call an API to the server and upon successful callback I need to transfer use to a new page, I am writing the onclick event function as follow but it doesn't work
import SignupPage from "views/SignupPage/SignupPage.js";
export default function MyPage(props) {
const [is_logged_in, set_is_logged_in] = useState(props.is_looged_in);
const [fname, set_fname] = useState(props.fname);
const [lname, set_lname] = useState(props.lname);
...
function goto(e){
axios.get('http://127.0.0.1:9000/api/is_logged_in/')
.then(res =>{
if (res.status!=200) {
throw new Error('Network response was not ok');}
return res;})
.then(res=>{
const value=res.data.res;
set_is_logged_in(value);
if (value==1){
//traverse to new page upon successful login
var uploadScreen=[];
uploadScreen.push(<SignupPage />);
}
})
}
...
<Button onClick={goto} color="primary">Evaluate</Button>
}
Can someone help to come up with correct codes?

Related

Fresh Call To API EndPoint on Button Click in React JS

I haven API endpoint, that gives me a random text, on each call. As of now, when the React Component loads for the first time, a call goes through to the API and I get the random text.
The code looks like this. I am using redux for state management.
const RandomQuoteList = ({ todo, isLoading, startLoadingTodos }) => {
useEffect(() => {
startLoadingTodos();
}, []);
const [inputValue, setInputValue] = useState(`HelloThere`);
function changeRandomText(e) {
// const item = e.target.value;
var something = Math.random();
console.log(`changeRandomText clicked + ${something}`);
setInputValue(`changeRandomText clicked + ${something}`);
console.log(inputValue);
}
const loadingMessage = <div>Loading todos...</div>;
const content = (
<GeneralWrapper>
<RandomQuoteItem todo = {todo} inputValue = {inputValue}/>
<Button onClick={changeRandomText}>Get A New Quote</Button>
</GeneralWrapper>
);
return isLoading ? loadingMessage : content;
};
const mapStateToProps = state => ({
isLoading: getTodosLoading(state),
todo: getTodos(state),
});
const mapDispatchToProps = dispatch => ({
startLoadingTodos: () => dispatch(loadTodos()),
});
export default connect(mapStateToProps, mapDispatchToProps)(RandomQuoteList);
Now, I want to be able to use a simple button click to 'refresh' the API call. That way, the API endpoint will be triggered and a fresh new text will get updated.
I have looked at the following stack over flow questions.
React: re render componet after button click
How to refresh React page/component on Button click after POST
ReactJs : How to reload a component onClick
But, I am not getting far. I am able to randomly change the state of a text component, and that is changing the text component. So, I have the random value change part taken care of.
The target component looks something like this. When I click the button on the above component, the below component updates the random text no problem.
const RandomQuoteItem = ({ todo,inputValue }) => {
//set the style for the display.
// const Container = todo.isCompleted ? TodoItemContainer : TodoItemContainerWithWarning;
const Container = TodoItemContainer;
return (
<Container>
{/* this is where you show your API response single items. */}
<h3>{todo.quoteContent}</h3>
<h4>{todo.quoteAuthor}</h4>
<h4>{todo.quoteIdentifierString}</h4>
<p>-------------------------------</p>
<h4>{todo.quoteIdentifierCompadre}</h4>
<h4>{todo.dateTimeOfResponse}</h4>
<h4>{todo.detailsAboutOperation}</h4>
<p>{inputValue}</p>
</Container>
);
}
Now, how do I link this random state change to my RandomQuoteItem state, so, it makes fresh data call?
Based on the comment from rahuuz above, I ended up with this. and it worked.
function changeRandomText(e) {
// const item = e.target.value;
var something = Math.random();
console.log(`changeRandomText clicked + ${something}`);
setInputValue(`changeRandomText clicked + ${something}`);
console.log(inputValue);
startLoadingTodos(); //this specific line solved the problem.
}
I think, it worked in my favour that I already had redux and reducers and all of that hooked up. If that was no the case, this specific solution may not have worked, I think.
Button click calls the startLoadingTodos function, which in turn calls the API and that returns data, updating the redux state, and component also updates.

React component content disappears after page refresh

I am new to react and am having trouble figuring out why the data inside my Content component does not re-render on refresh.
When I visit one of the routes, say /sentences-of-the-day, and then I refresh the page, it seems all the stuff inside content is gone.
Can someone please help me out?
Here is the code sandbox:
https://codesandbox.io/s/mainichome-v7hrq
You need to load the data once the component is mounted (using useEffect) set to local state to trigger the render. In each refresh, mounting happens again and you have the data after each refresh.
Define another function in content.data.js
export const getContentData = () => {
return Promise.all(contentDataStories.map((func) => func()));
};
In your content.component.jsx
import { getContentData } from "./content.data.js";
const [content, setContent] = useState([]);
useEffect(() => {
(async () => {
setContent(await getContentData());
})();
}, []);
Code sandbox => https://codesandbox.io/s/mainichome-forked-4sx5n?file=/src/components/content/content.component.jsx:302-449
The problem is here:
import contentData from "./content.data.js";
//...
const [content] = useState(contentData);
That imports contentData and then sets it as state.
However, that value is asynchronous.
const contentData = [];
contentDataStories.forEach(function (func) {
func().then((json) => {
contentData.push(json);
});
});
export default contentData;
It's just [] until those promises reoslve.
So what's happening is that the page is loading fine, but the content from that file hasn't loaded before the first render.
This is a race condition. Which will happen first, the data loading or the render? Sometimes the render wins and everything is fine, but sometimes it doesn't and you get a blank page.
To fix it, you need to make React aware of your data loading, so that it can re-render when the data finishes loading.
First make a function that does your async loading:
export function getContentData() {
return new Promise((resolve) => {
// fetch async stuff here
resolve(myDataHere)
})
}
And then call that from a useEffect, which sets the state.
function Content() {
const { titleParam } = useParams();
const [content, setContent] = useState(contentData);
useEffect(() => {
getContentData().then(setContent);
}, [getContentData]);
//...
}
Now when you component mounts, it calls getContentData. And then that promise resolves, it sets the state, triggering a a new render.

How do I call props.history.push() if I'm destructuring my props?

If I've got a function that creates a confirm popup when you click the back button, I want to save the state before navigating back to the search page. The order is a bit odd, there's a search page, then a submit form page, and the summary page. I have replace set to true in the reach router so when I click back on the summary page it goes to the search page. I want to preserve the history and pass the state of the submitted data into history, so when I click forward it goes back to the page without error.
I've looked up a bunch of guides and went through some of the docs, I think I've got a good idea of how to build this, but in this component we're destructuring props, so how do I pass those into the state variable of history?
export const BaseSummary = ({successState, children}: BaseSummaryProps) => {
let ref = createRef();
const [pdf, setPdf] = useState<any>();
const [finishStatus, setfinishStatus] = useState(false);
const onBackButtonEvent = (e) => {
e.preventDefault();
if (!finishStatus) {
if (window.confirm("Your claim has been submitted, would you like to exit before getting additional claim information?")) {
setfinishStatus(true);
props.history.push(ASSOCIATE_POLICY_SEARCH_ROUTE); // HERE
} else {
window.history.pushState({state: {successState: successState}}, "", window.location.pathname);
setfinishStatus(false);
}
}
};
useEffect(() => {
window.history.pushState(null, "", window.location.pathname);
window.addEventListener('popstate', onBackButtonEvent);
return () => {
window.removeEventListener('popstate', onBackButtonEvent);
};
}, []);
Also I'm not passing in the children var because history does not clone html elements, I just want to pass in the form data that's returned for this component to render the information accordingly
first of all, I think you need to use "useHistory" to handling your hsitry direct without do a lot of complex condition, and you can check more from here
for example:
let history = useHistory();
function handleClick() {
history.push("/home");
}
now, if you need to pass your history via props in this way or via your code, just put it in function and pass function its self, then when you destruct you just need to write your function name...for example:
function handleClick() {
history.push("/home");
}
<MyComponent onClick={handleClick} />
const MyComponent = ({onClick}) => {....}
I fixed it. We're using reach router, so everytime we navigate in our submit forms pages, we use the replace function like so: {replace: true, state: {...stateprops}}. Then I created a common component that overrides the back button functionality, resetting the history stack every time i click back, and using preventdefault to stop it from reloading the page. Then I created a variable to determine whether the window.confirm was pressed, and when it is, I then call history.back().
In some scenarios where we went to external pages outside of the reach router where replace doesn't work, I just used window.history.replaceStack() before the navigate (which is what reach router is essentially doing with their call).
Anyways you wrap this component around wherever you want the back button behavior popup to take effect, and pass in the successState (whatever props you're passing into the current page you're on) in the backButtonBehavior function.
Here is my code:
import React, {useEffect, ReactElement} from 'react';
import { StateProps } from '../Summary/types';
export interface BackButtonBehaviorProps {
children: ReactElement;
successState: StateProps;
}
let isTheBackButtonPressed = false;
export const BackButtonBehavior = ({successState, children}: BackButtonBehaviorProps) => {
const onBackButtonEvent = (e: { preventDefault: () => void; }) => {
e.preventDefault();
if (!isTheBackButtonPressed) {
if (window.confirm("Your claim has been submitted, would you like to exit before getting additional claim information?")) {
isTheBackButtonPressed = true;
window.history.back();
} else {
isTheBackButtonPressed = false;
window.history.pushState({successState: successState}, "success page", window.location.pathname); // When you click back (this refreshes the current instance)
}
} else {
isTheBackButtonPressed = false;
}
};
useEffect(() => {
window.history.pushState(null, "", window.location.pathname);
window.addEventListener('popstate', onBackButtonEvent);
return () => {
window.removeEventListener('popstate', onBackButtonEvent);
};
}, []);
return (children);
};

In React Query, How to get data if i'm using two mutate functions in a component?

I'm on a component where a api call exists on componentdidmount and one when use clicks on a button of that page so how can get the data of that particular api call?
const [mutate, { data, error }] = useMutation(handlefunction);
componentDidMount(){
mutate({api:api, data:{email:"email});
}
return(
<button onClick = {()=>{mutate({api:api2, data:{}})}>click</button>
}
how can i get data for button click mutate?
onClickHandler = (e) => {
e.preventDefault();
mutate({api:api2, data:{}})
}
In return function:
<button onClick = {onClickHandler}>click</button>

Component shows previous data when mount for fractions of seconds

I am developing an app named "GitHub Finder".
I am fetching the date in App component using async function and pass these function to User component as props and I call these functions in useEffect.
The problem is here, when I goto user page for second time it shows previous data which I passed in props from App component and then it shows loader and shows new data.
Here is App component code where I am fetching date from APIs and passing to User component through props.
// Get single GitHub user
const getUser = async (username) => {
setLoading(true);
const res = await axios.get(
`https://api.github.com/users/${username}?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID}&client_secret=${
process.env.REACT_APP_GITHUB_CLIENT_SECRET}`
);
setUser(res.data);
setLoading(false);
}
// Get user repos
const getUserRepos = async (username) => {
setLoading(true);
const res = await axios.get(
`https://api.github.com/users/${username}/repos?
per_page=5&sort=created:asc&client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID}&client_secret=${
process.env.REACT_APP_GITHUB_CLIENT_SECRET}`
);
setRepos(res.data);
setLoading(false);
}`
User component code.
useEffect(() => {
getUser(match.params.login);
getUserRepos(match.params.login);
// eslint-disable-next-line
}, []);
I've recorded a video, so you guys can easily understand what I am trying to say.
Video link
Check live app
How can I solve this problem?
Thank in advance!
Here is what happens in the app :
When the App component is rendered the first time, the state is user={} and loading=false
When you click on a user, the User component is rendered with props user={} and loading=false, so no spinner is shown and no data.
After the User component is mounted, the useEffect hooks is triggered, getUser is called and set loading=true (spinner is shown) then we get the user data user=user1 and set loading=false (now the user data is rendered)
When you go back to search page, the app state is still user=user1 and loading=false
Now when you click on another user, the User component is rendered with props user=user1 and loading=false, so no spinner is shown and the data from previous user is rendered.
After the User component is mounted, the useEffect hooks is triggered, getUser is called and set loading=true (spinner is shown) then we get the user data user=user2 and set loading=false (now the new user data is rendered)
One possible way to fix this problem :
instead of using the loading boolean for the User component, inverse it and use loaded
When the User component is unmounted clear the user data and the loaded boolean.
App component:
const [userLoaded, setUserLoaded] = useState(false);
const getUser = async username => {
await setUserLoaded(false);
const res = await axios.get(
`https://api.github.com/users/${username}?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`
);
await setUser(res.data);
setUserLoaded(true);
};
const clearUser = () => {
setUserLoaded(false);
setUser({});
};
<User
{...props}
getUser={getUser}
getUserRepos={getUserRepos}
repos={repos}
user={user}
loaded={userLoaded}
clearUser={clearUser}
/>
User component:
useEffect(() => {
getUser(match.params.login);
getUserRepos(match.params.login);
// eslint-disable-next-line
return () => clearUser();
}, []);
if (!loaded) return <Spinner />;
You can find the complete code here
Please make your setUser([]) empty at the start of getUser like this:
const getUser = async (username) => {
setLoading(true);
setUser([]);
const res = await axios.get(
`https://api.github.com/users/${username}?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID}&client_secret=${
process.env.REACT_APP_GITHUB_CLIENT_SECRET}`
);
setUser(res.data);
setLoading(false);
}

Resources