Initiate function on page load - reactjs

I have a function that gets some data when it is initiated. This can be done by a click even on a button, e.g.:
<button type="button" onClick={fetchData}>Get data</button>
Basically it is just a function given by:
function fetchData(e) {
e.preventDefault();
<something POST to get data>
}
All of this is working as intended. However, the data is only fetched when the button is clicked. I would like for the data to be fetched on load as well so there is actually some data presented before the button is clicked.
I thought I could just say:
useEffect(() => {
fetchData()
}, [])
But that doesn't seem to work. However, if I take everything inside the function, and replace it with the fetchData() in the useEffect, then it works. But I don't feel like changing stuff twice every time the function needs updates etc.
So what am I doing wrong ?

That is because when you're calling fetchData() in useEffect, no argument is being provided to the function. Yet in the function you are expecting the first argument to be present and calling it with e.preventDefault(). Since e will be undefined, attempting to access a key/property inside undefined will throw an error.
There are two solutions:
Only invoke functions stored in e when it is not undefined
Write a separate handler for the click event binding
Solution 1: Use optional chaining
By first checking if e is null or undefined, we can prevent attempting to access a key/property on it:
function fetchData(e) {
e?.preventDefault();
// Additional fetching logic
}
If you want to also support browsers that do not support optional chaining, then using the logical AND operand (&&) to perform lazy evaluation / short-circuiting will also work:
function fetchData(e) {
e && e.preventDefault();
// Additional fetching logic
}
Solution 2: Separate event handlers for button
Remove e from your fetchData function completely:
function fetchData() {
// Additional fetching logic
}
...and prevent the default event at the level of the click handler:
const onClick = (e) => {
e.preventDefault();
fetchData();
}
<button type="button" onClick={onClick}>Get data</button>

Related

React context not being up to date in a Timeout

I am using react app context to store an array of "alerts objects" which is basically any errors that might occur and I would want to show in the top right corner of the website. The issue I am having is that the context is not being up to date inside a timeout. What I have done for testing is gotten a button to add an alert object to the context when clicked and another component maps through that array in the context and renders them. I want them to disappear after 5 seconds so I have added a timeout which filters the item that got just added and removes it. The issue is that inside the timeout the context.alerts array seems to have the same value as 5 seconds ago instead of using the latest value leading to issues and elements not being filtered out. I am not sure if there's something wrong with my logic here or am I using the context for the wrong thing?
onClick={() => {
const errorPopup = getPopup(); // Get's the alert object I need
context.setAlerts([errorPopup, ...context.alerts]);
setTimeout(() => {
context.setAlerts([
...context.alerts.filter(
(element) => element.id !== errorPopup.id,
),
]);
}, 5000);
}}
onClick={() => {
const errorPopup = getPopup(); // Get's the alert object I need
context.setAlerts([errorPopup, ...context.alerts]);
setTimeout(() => {
context.setAlerts(alerts => [
...alerts.filter(
(element) => element.id !== errorPopup.id,
),
]);
}, 5000);
}}
This should fix it. Until react#17 the setStates in an event handler are batched ( in react#18 all setStates are batched even the async ones ), hence you need to use the most fresh state to make the update in second setAlerts.
To be safe it's a good practice using the cb syntax in the first setState as well.
I think the fix would be to move context.setAlerts(...) to a separate function (say removePopupFromContext(id:string)) and then call this function inside the setTimeout by passing the errorPopup.Id as parameter.
I'm not sure of your implementation of context.setAlerts, but if it's based on just setState function, then alternatively, you could do also something similar to how React let's you access prevState in setState using a function which will let you skip the creation of the extra function which may lightly translate to:
setContext(prevContextState =>({
...prevContextState,
alerts: prevContextState.alerts.filter(your condition)
)})

Not able to trigger function from event

I am not a skilled react programmer but still hope someone would care to explain what I am missing:
What I want
I would like to change accounts in Metamask, detect the "accountsChanged" event, and trigger the testFunction.
What works
I am able to trigger the testFunction by clicking the test function button.
I can detect account change (for some reason it is detected around 5 times every time I change).
What does not work
I am not able to trigger the testFunction upon account change and get the message TypeError: this.testFunction is not a function
Suspect there is something fundamental about react I am missing here...Thanks for all replies!
class App extends Component {
...
componentDidMount = async () => {
...
};
testFunction = async =>{
console.log("triggered the test function");
};
render() {
window.ethereum.on('accountsChanged', function (accounts) {
console.log("account change detected");
this.testFunction(); --> this is not working
});
return (
<div className="App">
<button type="button" onClick={this.testFunction}>test function</button>
</div>
);
}
}
You need to convert your normal function to arrow function. Because normal function derives this from the object which is calling it, but arrow function derives it's this from surrounding scope, hence in arrow function this will point to your class and will have access to the methods.
window.ethereum.on('accountsChanged', accounts => {
Also, you can continue using normal function, but in that case you can store the this in some other variable like that' or 'self and use it inside the normal function to call the methods of the class.
let that = this;
window.ethereum.on('accountsChanged', function(accounts){
that.testFunction() //this will work
I struggled to update the component of my app when an account was changed using MetaMask. What I did was what Vivek suggested: create a reference of this and then handle the callback. At the end my function using etherjs and the same event of metamask (ethereun.on('accountsChanged'..was this
const here = this
provider.provider.on('accountsChanged', function (accounts) {
console.log('Account changed!!')
here.currentAccount = accounts[0]
})
This code also work with Vue

Is there a way to put requirements on hook dependencies?

Currently, I am checking if the input useEffect() hook dependency is at least a certain length before calling loadSearchData() which is an async method that hits an API.
useEffect(() => {
if (input.length >= MIN_CHAR_INPUT) {
loadSearchData();
}
}, [input]);
Is there a way where I could move the input check to the input dependency param for useEffect()? Probably a case where I need to write a custom hook.
Is there a way where I could move the input check to the input dependency param for useEffect()? Probably a case where I need to write a custom hook.
I'd build it this way:
function useEffect2(effect, deps, isValid = true) {
const cleanup = React.useRef(null);
useEffect(() => {
if (isValid) {
if (typeof cleanup.current === "function") {
// schedule cancellation of previous request right after effect has been called
// using the Promise construct here so I don't have to deal with a throwing cancellation function
Promise.resolve().then(cleanup.current);
}
// in case effect() throws,
// don't want to call the old cancellation function twice
cleanup.current = null;
// get new cancel-function
cleanup.current = effect();
}
}, deps);
useEffect(() => () => {
// deal with cancellation on unmount
typeof cleanup.current === "function" && cleanup.current();
}, []);
}
useEffect2(loadSearchData, [input], input.length >= MIN_CHAR_INPUT);
I just want to clarify the cancel. This will give us access to the current useEffect() call in the stack and allow us to properly handle the call without any memory-leaks
From https://reactjs.org/docs/hooks-effect.html#recap
We’ve learned that useEffect lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function
Cleanup is probably a better name. I use it the most to "cancel" previous ajax-requests if they are still pending/prevent them to update the state. I've renamed the variable in the code.
What we're trying to emulate here is a useEffect that runs the effect conditionally. So when the condition is false, we don't want the effect to cleanup the previous call; as if the deps didn't change. Therefore we need to handle the cleanup function ourselves, and when/wether it should be invoked. That's
when (and only if) we call the effect function
on componentWillUnmount
That's what this ref is for. Since the reference is overwritten with every call to effect this shouldn't leak any memory.

Understanding the difference between this.function() and this.function

Hi I have been reading on react as well as doing some coding and I can't help but notice that in certain portions of the code we will use this.function() and in some we will call using this.function, I do not understand what is the difference between them and how do i determine when should i call with () and when should i not.
For example I can have the following code which will use this.function
//Arrow function used to bind the necessary variables
CallFunction = (event) =>{
console.log("Event was called");
}
render(){
return(
<form onSubmit={this.CallFunction} />
);
}
Next I can have the following code
CallFunction () {
console.log("Event was called");
}
render(){
return(
<div>{this.CallFunction()}</div>
);
}
This is just plain ole javascript, it's the difference between executing a function and just referencing a function. If you have the parens, that function will get executed immediately upon render. So in your second example you would see 'Event was called' immediately in your console when this component mounts. There are several reasons why we might have to do this, a simple one is that we just want to break out some of our code to a new function to make things easier to read.
With callbacks we don't want to execute the function right away, so we don't use the parens. We are just referencing the function. We're basically saying here's the function I want you to execute when someone submits this form. If we used the parens, that function would execute immediately on the component mounting - not wait until the form is submitted.
edit: Sometimes you need to have a function execute on render and then return a new function you want to execute as an event handler callback:
function thisRunsOnRender () {
return function thisRunsOnSubmit (e) {
console.log(e) // this would be the submit event
}
}
....
render () (
<form onSubmit={thisRunsOnRender()} />
)

fuction not returning a list of radio button Reactjs- ReactBootstrap

I am working on a dash board, that fetches data from acuity scheduling.
I am making a form and using a function to get list of radio button:
following array has it, just to clarify. It takes time to get the value in from the API so I have used setTimeout, in the function:
setTimeout(() => {
return timeForID.map( obj => {
return (<Radio value={obj.date}>{obj.hours}:{obj.mins}</Radio>);
})
}, 500)
I am getting a blank space in the place of radio buttons.
There are a lot of answers in JavaScript out there about working with the event loop and callbacks -- See: How do I return the response from an asynchronous call?
Essentially, your return inside of setTimeout doesn't go anywhere.
In order to trigger another render in your component, you will have to use setState. You can call setState after your API call completes -- you shouldn't have to use setTimeout. Let's say you're using fetch to get the API:
fetch(apiUrl).then(response => response.json()).then(dates => setState({ dates }))
Now in your render function you can have:
{this.state.dates.map(({ date, hours, mins }) => (
<Radio value={date}>{hours}:{mins}</Radio>
)}
Initialize the dates property of state to an empty array to prevent errors on the initial load.

Resources