React ovverride shown component with new component after server response - reactjs

I would want load new component after server response using React. Its could be a simple operation but something does not working at the moment. My code is the following:
const formSubmit = async e => {
e.preventDefault();
const response = await dataSubmit({
toSend
});
if(response.status === 200){
console.log("status 200");
return(
<>
<ComponentPage />
</>
);
}
else{
console.log("error status");
return(
<>
<ComponentPage />
</>
);
}
}
async function dataSubmit(toSend) {
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(toSend)
})
.then(data => (data.json()))
}
I tried removing e.preventDefault() or using effect but is not working. This component will have to ovverrife atually Form component shown. console.log lines works. How can I solve? Thanks

returning a component as part of formSubmit or a clickHandler won't render the component.
the simplest way i can think of doing is to, make the render() or the default return of this component to be conditional.
have a loading state, set true when the form is submitted and false when you get the response. render your <Component/> only if loading is false.
something like this,
const YourComponent = () => {
const [loading, setLoading] = useState(false);
//set loading when form is submitted.
// unset it when the response is recieved.
return <>
{loading} ? <Loader/> : <Component/>
</>
}
you can extend the same principle to show errors as well.

Related

React state not updated and error "Can't perform a React state update on an unmounted component"

I'm trying to create an app that once a user logs in (enters a partyID), it directs them to a new page where it pulls back all the users data. Once they 'Log In; the new page isn't pulling the data as I expected.
However when I write to console the data is says undefined but the fetch URL does work when i go to it locally on my browser.
enter image description here
Here is my code
class CalcForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
this.setState({ isLoading: true });
const serachByCustomerId = this.props.location.state;
const url =
"<MYURL>/api/Customer/" +
serachByCustomerId;
console.log("URL Being used ", url);
fetch(url)
.then((res) => res.json())
.then((data) => this.setState({ data: data }))
if (!this.state.isLoading) {
console.log("data after search", this.state.data);
}
}
// renders to display on page
render() {
const { data, isLoading } = this.state;
// if page is loading displays loading text and spinner to make user awear
if (isLoading) {
return (
<div className="pageLoading">
<p>Loading...</p>
<FadeLoader size={150} color={"#2d8259"} loading={isLoading} />
</div>
);
}
return (
<div> hi </div>
);
}
}
export default CalcForm;
I was expected the data returned to be printed into the console but upon looking I get undefined and there is also an error I don't understand
setState is asynchronous, so if you want to console.log the data, it must be within a callback:
this.setState({key: value}, () => {console.log(value})
This is what your componentDidMount() would look like:
componentDidMount() {
this.setState({isLoading: true });
const searchByCustomerId = this.props.location.state;
const url = "<MYURL>/api/Customer/" + searchByCustomerId;
console.log("URL Being used ", url);
fetch(url)
.then((res) => res.json())
.then((data) => this.setState({data: data },
() => {
console.log("data after search", data);
this.setState({isLoading: false})
}
))
}
PLUS: you had a typo (search not serach)
Hope this helps :)
Why not go down the hooks approach? Its far more nicer and easier to do things:
psuedo code to get you going. It has an await function so you should be able to derive your data once you pass in your url.
export default function CalcForm() {
const [isLoading, setLoading] = React.useState(true);
const [data, setData] = React.useState(false);
const getData = async () => {
setLoading(true);
const response = await fetch(url);
setData(response.json());
setLoading(false);
};
React.useEffect(() => {
getData();
}, []);
if (isLoading) {
return (
<div className="pageLoading">
<p>Loading...</p>
<FadeLoader size={150} color="#2d8259" loading={isLoading} />
</div>
);
}
return <div className="pageLoading">hi</div>;
}

Why the flag is showed every time I reload the page after client has been created?

What I have done by far is when a user creates a client, in the top right of the page, is shown a flag(notification), which says "Client has been successfully created".
To do that was a little complex for me, because saving the client to DB, and listing the client to the web page are in two different components. Also, the flag is another component as well.
To save and list the clients I have used Axios since I'm dealing with the backend a lot.
SaveClient.js
export default function SaveClient({}) {
const save = async () => {
const clientParams = {
userName:
currentClient: clientName,
clientId: clientId,
};
await axios
.post(
process.env.REACT_API_CLIENT, clientParams
)
.then((response) => {
navigate("/clientlist", {state: {showFlagCreate: true}}); //passing the state
})
.catch((error) => {;
console.log(error);
});
};
}
ClientList.js
export default function ClientList() {
const { state } = useLocation();
const showFlagCreate = state?.showFlagCreate;
const [clientlist, setClientList] = useState([])
useEffect(() => {
const clientParams = {
userName:
currentClient: clientName,
clientId: clientId,
};
axios
.get(process.env.REACT_API_CLIENT, clientParams)
.then((response) => {
const {data} = response
setClientList(data)
})
.catch((error) => console.log(error));
}, []);
return (
<div>
...
{showFlagCreate && <FlagCreateClient />}
</div>
);
}
FlagCreateClient
export default function FlagCreateClient() {
const [show, setShow] = useState(true);
return (
<div>
<Transition
show={show}
as={Fragment}
<div>
<p>The client is successfully created.</p>
</div>
<div>
<button onClick={() => {setShow(false)}}>
<span>Close</span>
</button>
</div>
</Transition>
<div/>
);
}
The idea is that in the SaveClient component, when a client is saved, in .then() inside the navigate() function, pass a state in a true condition.
Then in the ClinetList component, I call the state using useLocation(), and I passed in the component {showFlagCreate && <FlagCreateClient />}.
By far this logic works, I create a client, the flag is shown after, but when I reload the page after, the flag is shown. When I make other actions in the webpage which might be clicking the edit button and going back to the ClientList component the flag won't show, even if I reload/refresh the page.
How can I fix this bug?

React passing data or adding to state wrong

When I leave this code as is, I will get the correct console.log (commented with "these appear correct") that I'm looking for. However when I replace the api_url with http://localhost:9000/ipdata/${this.state.inputValue} the console.log is blank. This is why I think I'm either passing the input value wrong or I'm adding it to the state wrong.
I would assume I'm adding it to the state wrong as the spans that I'm trying to render in order to output the data on the client aren't displaying anything either.
Heres my code ...
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: '', inputValue: '', result: {} };
}
async callAPI() {
try {
console.log('called API...');
const api_url = `http://localhost:9000/ipdata/8.8.8.8`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const result = await res.json();
// these appear correct
console.log(result.city);
console.log(result.region_code);
console.log(result.zip);
this.setState({ result });
} catch (error) {
// handle errors
}
}
render() {
return (
<div className="App">
<h1>IP Search</h1>
<input
type="text"
value={this.state.inputValue}
onChange={(e) => this.setState({ inputValue: e.target.value })}
/>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span>{this.state.result.city}</span>
<span>{this.state.result.region_code}</span>
<span>{this.state.result.zip}</span>
</p>
</div>
);
}
}
export default App;
API call on the Node server...
const fetch = require('node-fetch');
app.get('/ipdata/:ipaddress', async (req, res, next) => {
console.log(req.params);
const ipaddress = req.params.ipaddress;
console.log(ipaddress);
const api_url = `http://api.ipstack.com/${ipaddress}?access_key=API_KEY`;
const response = await fetch(api_url);
const json = await response.json();
res.json(json);
});
The problem is not the way you set state, but the way you access it, because callAPI doesn't have access to this, so you get an error thrown inside the function and as you don't handle errors, it gets swollen. To make it work you either bind the function
onClick={this.callAPI.bind(this)}
or use arrow function instead
callAPI = async ()=> {

Next.js, make POST request on button click (SSG)

I'm doing an ssg website, it doesn't have backend, but it does start to have some features that fetch some data from another server. I checked the SWR and it works, the issue is I need to make a post request on button click, and it gets me an error:
hooks can only be called inside of the body of a function component
What I see is that I can create a function component, set up a call in it and just mount this component on button click, it works, but I'm having doubts about this approach.
This is probably done to work with get request, but I make a post.
ExportComponent renders on a page or in another component.
function ExportComponent() {
const [exportCalled, setExportCalled] = useState(false)
const exportCall = () => {
setExportCalled(true)
}
if (exportCalled) {
return (
<CallExport></CallExport>
)
}
return (
<Button
onClick={ exportCall() }
>
Export
</Button>
);
}
function CallExport() {
// api post call
const { data, isLoading } = exportProject();
if (isLoading) {
return <CircularProgress />;
}
return (
// call to api is done, make something with data
<Button>Open</Button>
)
}
export function exportProject() {
const params = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({}),
};
const exportFetcher = (url) => fetch(url, params).then((r) => r.json());
const { data, error } = useSWR('url', exportFetcher);
return {
data,
isLoading: !error && !data,
isError: error
}
}
Is it wrong? Is there a better way?

react button is called before onPress when using Hooks

I am trying to request an API request using hooks. But my problem is that my function is called before I onPress.
I have an custom API component like this:
const FetchDataPut = (URL) => {
useEffect(() => {
async function fetchData() {
const res = await axios({
method: 'put',
url: URL,
data:{
name:'111',
email:'222',
password:'333',
id:'444',
phone:'555'
}
});
const response = await res;
console.log(response, 'completed')
}
fetchData()
},[])
return null
}
I could see from the console.log that api request has completed. My problem is that my api component is called before I onPress the button.
This is my Button component:
const EditAccount = (props) => {
const Desktop = () => {
const URL = `MyURL...`
return (
<View>
<Button title='edit account' onPress={FetchDataPut(URL)}/>
</View>
)
}
return(
<div>
<Desktop/>
</div>
)
}
If I change my onPress function to an arrow function like this:
onPress={()=>FetchDataPut(URL)} component isn't called before I onPress it. But it will give me an error Invalid hook call. Hooks can only be called inside of the body of a function component
Any Idea how to my the api request when I onPress the Button ?
Any comments or advice would be really helpful. Thanks in advance! :)
I think the way to go is to use a hook rather than a component:
const useFetchDataPut = () => {
const [url, setUrl] = useState("");
useEffect(() => {
if (!url) return;
async function fetchData() {
const res = await axios({
method: "put",
url,
data: {
name: "111",
email: "222",
password: "333",
id: "444",
phone: "555"
}
});
const response = await res;
console.log(response, "completed");
}
fetchData();
}, [url]);
return setUrl;
};
And then call it when you press the button. Also Desktop should be defined outside of the EditAccount component.
const Desktop = () => {
const setUrl = useFetchDataPut();
return (
<View>
<Button
title="edit account"
onPress={() => setUrl("https://...")}
/>
</View>
);
};
const EditAccount = props => {
return (
<div>
<Desktop />
</div>
);
};
Let me know if something is not clear.
There are several issues here.
You’re setting the onPress prop to the result of calling FetchDataPut(URL). The prop should be the function itself, not the result of invoking it. By using an arrow function you’re declaring a new function that, when invoked, calls FetchDataPut.
If you’re invoking it on button press, there’s no need for it to be a hook.
Also, FetchDataPut isn’t a React component.
Declare your data fetching function by itself:
async function fetchData(url) {
return axios({
method: 'put',
url: URL,
data: {
name:'111',
email:'222',
password:'333',
id:'444',
phone:'555'
}
})
}
And then invoke it on button press.
const handler = async function ( ) {
// or just inline the axios request right here
// instead of declaring a separate function for it.
const result = await fetchData(url);
// do something with the result
}
<Button onPress={handler} />
Apologies for the frequent edits. Trying to do this from my phone.

Resources