React onClick event trigger infinitely on render - reactjs

So i ran into some funny bug with React onClick event. So if I write something like this:
login = () => {
this.setState({ authed: true })
alert(this.state.authed)
}
render() {
return (
<div>
<Loginpage />
<button onClick={this.login}>test</button>
</div>
);
it would function normally but if onClick is changed to onClick={this.login()}, this event will be triggered on render and continue infinitely even after changing the code back and re-render this will still go on. I'm just curious why it ends up like that, does anyone knows?

If you use this onClick={this.login()} you are calling the function as soon as it renders and not onClick, that's why your component is infinitelly rendering.

So if you do this:
<button onClick={this.login}>test</button>
You are passing a reference to the function login on click of the button.
But if you do this:
<button onClick={this.login()}>test</button>
Then you are executing the function as soon as this element is rendered.
The login function triggers a state change:
login = () => {
this.setState({ authed: true })
alert(this.state.authed)
}
And since state change triggers another render, therefore it goes into the infinite loop.
Had you not called the setState in login, it would not go into the infinite loop.
Only your function will execute without even clicking the button on render.

Try this
<button onClick={()=>this.login}>test</button>

Related

can i ask using useEffect inside onFocus Event?

const [showSearch, setShowSearch] = useState(false);
const [inputHover, setInputHover] = useState(false);
...
<div className={`search ${showSearch ? "show-search" : ""} `}>
<button
onFocus={
setShowSearch(true)
}
onBlur={() => {
if (!inputHover) {
return setShowSearch(false);
}
}}
>
First I'm using just useState but
[Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.]
this error occurred
so I wrapped it inside useEffect then
[Warning: Expected onFocus listener to be a function, instead got a value of object type.]
I find this error children component receives props but just direct push event function
so.. finally upside code is complete...
but this code does not work.
help me, please
The error Expected onFocus listener to be a function, instead got a value of object type occurred because you tried to pass an object as an event handler instead of a function. You should pass a function as the event handler to avoid this error. So onFocus={ ()=> setShowSearch(true) } is the right way to do it.
[Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.] appeared because you called setShowSearch(true) in onFocus handler and every time when button rendered setShowSearch(true) executed and changed the state and it triggered another rerender and and setShowSearch(true) executed again and so it went on endlessly. When you put it in a callback form like onFocus={ ()=> setShowSearch(true)} it only gets executed when you focus the button
onFocus={ ()=> setShowSearch(true) }
i fixed right now.
but i didn't know why this code work no error.
who tell me why that code dosen't work and this code work?

Returning a function in useEffect

I'm trying to understand why returning a function inside useEffect hook executes during the second time. For example:
const App = (props) => {
const [change, setChange] = useState(false);
useEffect(() => {
return () => console.log("CHANGED!");
}, [change]);
return (
<div>
<button onClick={() => setChange(true)}>CLICK</button>
</div>
);
};
The first time the component renders, 'CHANGED' is not being logged, but after I click the button, it does. Does anyone know why?
Per the React docs - If your effect returns a function, React will run it when it is time to clean up. https://reactjs.org/docs/hooks-effect.html. The returned function will fire when the component unmounts.
If you use console.log() inside of useEffect but not in the return block, then it will be executed AFTER rendering. Putting it into return block means, you want use it as a clean-up function, so it'll be executed before the component unmounts or right before the next scheduled effect's execution. However, in your case, for the very first time, you neither have a scheduled effect nor something mounted. That's why you see the string "CHANGED" only when you click on the button.

Trying to automatically run a method without having the user click the button

{!this.props.account ? (
<button onClick={this.props.onSignIn}>Sign In</button>
) : (
// Otherwise show the homepage
Replacing the onClick with a way to just have the this.props.onSignIn happen just once.
The user should not have to click on a button to log in, but once the page loads, it should just check if the user is authenticated, and if not then run the onSignIn method just once
Any thoughts?
componentDidMount is the lifecycle hook to run code once after mounting:
componentDidMount() {
if (!this.props.account) {
this.props.onSignIn()
}
}
If you are using Hooks, useEffect is what you are looking for.
useEffect(() => {
if(!this.props.account) {
this.props.onSignIn();
}
}, [])
Notice the second argument of useEffect, it accepts a set of dependencies, upon the changes on those dependencies, the useEffect hook will run again.
Since we are passing nothing, (an empty array), the hook will run only once when the component mounts.
Read more about hooks: https://reactjs.org/docs/hooks-effect.html

State update on unmounting component - how to fix that?

I have some problems with updating state on an unmounted component as below:
All works as expected. Modal is closed after the comment is deleted. But I'm getting a warning:
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the
componentWillUnmount method
I know what causes the problem but I don't know how to fix that. Here's my data flow:
This function invokes an action responsible for deleting user posts:
async handleDelete() {
const { articleID, comment, deleteComment } = this.props;
await deleteComment({ articleID, idcomment: comment.idcomment });
//showModal flag is set to false - hide confirmation modal after clicking 'yes' button
this.setState({showModal: !this.state.showModal})
}
Action listed above 'deleteComment' looks like this:
export const deleteComment = data => async dispatch => {
const { idcomment, articleID } = data;
try {
await axios.post(`/api/comments/${idcomment}`, {_method: 'DELETE'});
//fetchling comments again to update a state and it looks like it causes the problem with updating a state on unmounted component because when I commented out that line, it didnt happen anymore.
await dispatch(fetchComments(articleID));
}
catch(e) {
throw new Error(e) }
finally {
dispatch(setCommentStatus(true));
dispatch(decCommentCount());
}
}
Not my question is, how to fix that? I want to close my confirmation modal after the comment is deleted from the database and the new sets of comments are already updated.
Here is how I use my modal:
<Modal
showModal={showModal}
accept={this.handleDelete}
denied={() => this.setState({showModal: !this.state.showModal})}
/>
And the last one is the modal itself:
return (
!showModal
? ''
: (
<Wrapper>
<ModalSection>
<Header>
<Title>Usunięcie komentarza</Title>
<ButtonExit onClick={denied}>❌</ButtonExit>
</Header>
<Text>Czy jesteś pewien, że chcesz usunąć ten komentarz? <br /> Nie będziesz mógł cofnąć tej operacji.</Text>
<Footer>
<div>
<Button onClick={denied}>Cofnij</Button>
<Button warning onClick={accept}>Usuń</Button>
</div>
</Footer>
</ModalSection>
</Wrapper>
)
)
Apparently it works after deleting this.setState({showModal: !this.state.showModal}) from handleDelete function. Weird thing is after finishing deleting comment my modal is closing and I don't know HOW.. Something changing showModal value to false... Still figuring out.
EDIT: IM DUMB. After deleting comment I'm fetching comment list again AND that component has state of showModal set to FALSE by default so when component is rerendering the state is default which is false.................. SORRY.
It is because you are trying to modify state of Modal component after its being closed. Instead you should maintain state of comment deletion in the Parent component which renders the Modal component instead. And update the state in Parent component using a callback. This callback can be passed to Modal component as a prop and called when a delete action occurs. This should fix the issue.
You shouldn't use tenary to render the modal (!showModal ? '' : <...>) You should pass a prop to your modal that only hides or shows the modal.
If you see some libraries they have a show prop or isOpen
e.g. react-bootstrap react-modal
They have that prop so you don't have errors like the one you are having.

How can I save the state of a React.Component on submit without reseting it to the default?

My idea is similar to a voting system: some blocks with a name, a number, and a button.
When I press the button the number is incremented, +1. The names come from an array in the state. How I call my component, being createAPP just a setState:
<AppForm handleCreate={this.createAPP} />
I am using react-router to do the routing:
<Link to="/">Home</Link>
<Route
exact path="/"
component={ListOfFrameworks}
/>
I want to place a textbox where I can type another name and it will create a similar box to the other boxes, with the same functionality.
I was using a form that onSubmit it stores to the parent state another name.
It draws everything correctly but then the page refreshes and resets to the original array.
<form onSubmit={() => this.submit} >
My submit function:
submit(e) {
e.preventDefault();
this.props.handleCreate(this.refs.name.value);
this.refs.name.value = "";
}
How can I keep the page without refreshing, or keep the state after refresh?
Use preventDefault() method inside onSubmit method to prevent default behaviior
onSubmit(e){
e.preventDefault()
}
In your onSubmit function, add a call to event.preventDefault()
function mySubmitFunction(evt) {
evt.preventDefault();
// ...
}

Resources