React parent property function not firing when called from child function - javascript

In my parent component i have a function called handleDocumentSubmission which i want to pass down to the child.
handleDocumentSubmission = input => async (e) => {
console.log("fired");
I then render the following component like this. I use the same function name.
<FrontConfirm
nextStep={this.nextStep}
handleReview={this.handleReview}
values={values}
handleDocumentSubmission={this.handleDocumentSubmission}
/>
Now in my child component i want to call this function from a child function on click of a button.
continue = () => {
console.log("clicked", this.props);
this.props.handleDocumentSubmission("front");
};
<Button onClick={this.continue}
variant="contained" color="primary">
Confirm
</Button>
Now the console log for clicked i can see with props that has my handleDocumentSubmission function. But the console.log from the parent function console.log("fired") Does not get called.

This happens because handleDocumentSubmission is a curried function that accepts 2 sets of parameters. By using the following syntax and passing your event param, it will work :
continue = ev => {
console.log("clicked", this.props);
this.props.handleDocumentSubmission("front")(ev);
};
Your function will also not need to be asynchrinous :
handleDocumentSubmission = input => e => {
console.log("fired");
}
The final syntax without the continue function (I assume you created it for testing) will be the following :
<Button onClick={this.props.handleDocumentSubmission("front")}
variant="contained" color="primary">
Confirm
</Button>
Using this, your function will receive both your value (front) and the event information when fired.
Having a synchronous function will not prevent it from returning a value :
handleDocumentSubmission = input => e => {
console.log("fired");
return 'success'
}
continue = ev => {
console.log("clicked", this.props);
const result = this.props.handleDocumentSubmission("front")(ev);
console.log(result)
};
If you really want it to be async, use the await keyword :
handleDocumentSubmission = input => async e => {
console.log("fired");
return /* A promise */
}
continue = async ev => {
console.log("clicked", this.props);
const result = await this.props.handleDocumentSubmission("front")(ev);
console.log(result)
};

Related

React conditional re-rerender of an array based on filter

I’m running into an error that I could use some help on
Basically, I have a react app that is executing an HTTP call, receiving an array of data, and saving that into a state variable called ‘tasks’. Each object in that array has a key called ‘completed’. I also have a checkbox on the page called ‘Show All’ that toggles another state variable called showAll. The idea is by default all tasks should be shown however if a user toggles this checkbox, only the incomplete tasks (completed==false) should be shown. I can get all tasks to display but can’t get the conditional render to work based on the checkbox click
Here’s how I’m implementing this. I have the HTTP call executed on the page load using a useEffect hook and available to be called as a function from other change handlers (edits etc.)
Before I call the main return function in a functional component, I’m executing a conditional to check the status of ’ShowAll’ and filter the array if it's false. This is resulting in too many re-render errors. Any suggestions on how to fix it?
See simplified Code Below
const MainPage = () => {
const [tasks, setTasks] = useState([]); //tasks
const [showAll, setShowAll] = useState(true); //this is state for the checkbox (show all or just incomplete)
useEffect( ()=> {
axios.get('api/tasks/')
.then( response => { //this is the chained API call
setTasks(response.data.tasks);
})
.catch(err => {
console.log('error');
})
}, []);
const fetchItems = (cat_id) => {
axios.get('/api/tasks/')
.then( response => {
setTasks(response.data.tasks);
})
.catch(err => {
console.log('error');
})
};
//change the checkbox state
const handleCheckboxChange = (e) => {
setShowAll(!showAll)
console.log('Checkbox: ', showAll)
};
//this part updates the tasks to be filtered down to just the incomplete ones based on the checkbox value
if (showAll === false) {
setTasks(tasks.filter(v => v['completed']===false)); //only show incomplete tasks
}
return (
<div>
<label className="checkb">
<input
name="show_all"
id="show_all"
type="checkbox"
checked={showAll}
onChange={handleCheckboxChange}
/> Show all
</label>
<br/>
{ tasks && tasks.map((task, index) => {
return (
<div key={index} className="task-wrapper flex-wrapper">
<div >
{ task.completed === false ? (
<span> {index +1}. {task.task_description} </span> ) :
(<strike> {index +1}. {task.task_description} </strike>) }
</div>
<div>
<button
onClick={()=> modalClick(task)}
className="btn btn-sm btn-outline-warning">Edit</button>
<span> </span>
</div>
</div>
)
})}
</div>
);
};
export default MainPage;
Thanks
Two things to fix:
Use the checked property on event.target to update the state:
const handleCheckboxChange = ({target: { checked }}) => {
setShowAll(checked)
};
Filter as you want but don't update the state right before returning the JSX as that would trigger a rerender and start an infinite loop:
let filteredTasks = tasks;
if (!showAll) {
filteredTasks = tasks?.filter(v => !v.completed));
}
and in the JSX:
{ tasks && tasks.map should be {filteredTasks?.map(...
use e.target.value and useEffect :
//change the checkbox state
const handleCheckboxChange = (e) => {
setShowAll(e.target.checked)
console.log('Checkbox: ', showAll)
if (!e.target.checked) {
let list =tasks.filter(v => v.completed===false);
setTasks(list ); //only show incomplete tasks
}
};
or
//change the checkbox state
const handleCheckboxChange = (e) => {
setShowAll(e.target.checked)
console.log('Checkbox: ', showAll)
};
useEffect(()=>{
if (showAll === false) {
let list =tasks.filter(v => v.completed===false);
setTasks(list ); //only show incomplete tasks
}
},[showAll])

Difference between using Arrow function or function for onClick in ReactJS?

I built a simple counter app in ReactJS. Code is below.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [countNum, setCountNum] = useState(0);
function increaseCount() {
setCountNum(countNum + 1);
}
function decreaseCount() {
if (countNum > 0) {
setCountNum(countNum - 1);
}
}
function disableChecker() {
if (countNum === 0) {
return true;
} else {
return false;
}
}
return (
<div className="App">
<button onClick={() => decreaseCount()} disabled={disableChecker()}>Decrease</button>
<button onClick={() => increaseCount()}>Increase</button>
<h2>{countNum}</h2>
</div>
);
}
I just want to know why does onClick={() => increaseCount()} works AND why onClick={increaseCount()} or onClick={() => increaseCount} doesn't work?
A beginner here, please guide me to the answer.
onClick={() => increaseCount()} -> assigns a function as an event handler to onclick. The function's body has increaseCount() inside it. So when the function runs (on event trigger), increaseCount is executed/run.
onClick={increaseCount()} -> React runs increaseCount as soon as this code is encountered. increaseCount changes state and causes a re-render, and in the next render cycle same thing happens causing a cycle. This should have infinite renders.
onClick={() => increaseCount} -> Like the first one but here inside the function body, () is missing after increaseCount. This does not execute the function increaseCount when the event happens. A simple statement with function name without the parentheses will do nothing.
Why is my function being called every time the component renders?
Make sure you aren’t calling the function when you pass it to the component:
render() {
// Wrong: handleClick is called instead of passed as a reference!
return <button onClick={handleClick()}>Click Me</button>
}
Instead, pass the function itself (without parens):
render() {
// Correct: handleClick is passed as a reference!
return <button onClick={handleClick}>Click Me</button>
}
You can use an arrow function to wrap around an event handler and pass parameters:
<button onClick={() => handleClick(id)} />

Does a closure over a React event handler save my component from unecessary rerenders?

I read that anonymous function callbacks in JSX event handlers are a performance anti-pattern because they create unnecessary rerenders. Someone suggested that I should instead use a named function. No problem for a typical case where I only care about the event being passed in. The problem in my use case is that I need to pass additional parameters in to this callback.
I can't change onClick={handleClick} to onClick={handleClick(additionalParam)} because now I've got a function invocation, not a function reference.
So I started thinking, "what if that invocation returns a function with the signature I want?"
The solution I came up with involved using a closure over the callback with the desired additional parameters. I have an example below. Does this save me from rerenders? If not, what's wrong or how can I fix this?
Original version with anonymous function
const StudentButtonBlock = () => {
const handleClick = (e, analyticsData) => {
fireAnalytics('click: student button', {
...analyticsData
});
};
return (
<div className="student-button-block">
{students.map(student => (
<button
key={student.name}
handleClick={e => handleClick(e, { source: student.name })}>
Click Me
</button>
))}
</div>
);
};
Modified version with closure
const StudentButtonBlockModified = () => {
const handleClick = analyticsData => {
fireAnalytics('click: student button', {
...analyticsData
});
};
const handleClickWithStudent = student => {
return () => {
handleClick({ source: student.name });
};
};
return (
<div className="student-button-block">
{students.map(student => (
<button
key={student.name}
handleClick={handleClickWithStudent(student)}>
Click Me
</button>
))}
</div>
);
};

How to pass variables to a function in react

I want to pass parameters to deleteUndone, so that when the user activates completeTodo it calls deleteUndone with specified parameters, but can't figure out how?
deleteUndone = e => {
do something
}
completeTodo = e => {
completeUndone(e.target.id)
do more stuff ...
}
One way of going about it is to create a new function in the render method and pass along the event with any other argument you need.
Example
class App extends React.Component {
deleteUndone = (e, param) => {
// do something...
};
completeTodo = e => {
completeUndone(e.target.id);
// do more stuff...
};
render() {
return (
<div>
{/* ... */}
<button onClick={e => this.deleteUndone(e, "foo")}> Click me </button>
{/* ... */}
</div>
);
}
}
deleteUndone=(parameter1,parameter2)=>{
//do some stuff
}
To call deleteUndone use this operator.Assuming that a,b are defined in the state of the component.The arguments may also be defined in within the function scope.
completeTodo = e => {
const {a,b}=this.state;
completeUndone(e.target.id)
this.deleteUndone(a,b);
}

react formik, test if submit function called on click - jest,enzyme

I am testing a react component which contains a form(using formik) and I need to test if on submit button clicked, whether submit function is called or not.
at the moment, the test is failing.
now, the form has required fields schema too using yup
so, I was wondering whether I need to fill up all the fields before testing it.
because at the moment, it doesnt submit until the form has errors i.e. if the required fieldss are empty. so does that obstruct the testing of the button click and the function being called or not?
describe('submitform', () => {
let wrapper = '';
const handleSubmit = jest.fn();
beforeEach(() => {
wrapper = mount(<ExampleButton >
<span className="visible-sm">Next</span>
<span className="visible-xs font-entity">
›
</span>
</ExampleButton>
);
});
afterEach(() => {
wrapper.unmount();
});
it('call function on click',async ()=> {
// let btn = wrapper.find('#btnEx').find('button').find('#btnEx');
let btn = wrapper.find('button').simulate('click');
console.log('wrapper : ',btn.debug());
// btn.props().onClick();
expect(handleSubmit).toHaveBeenCalled();
});
})
how do I fill up the fields then, before testing? or is it even required for me to fill up the fields before testing on click?
You need a way to pass your mock handleSubmit function to your ExampleButton
If ExampleButton has onSubmit event handler prop, this is easier:
// ExampleButton.jsx
const ExampleButton = ({ onSubmit }) => <button type="submit" onClick={onSubmit} />;
// ExampleButton.test.jsx
const handleSubmit = jest.fn();
...
wrapper = mount(<ExampleButton onSubmit={handleSubmit} />);
If ExampleButton has inner event handler function, kinda tricky
// ExampleButton.jsx
const ExampleButton = () => {
const handleSubmit = (params) => {...}
return <button type="submit" onClick={handleSubmit} />;
}
// ExampleButton.test.jsx
wrapper = mount(<ExampleButton onSubmit={handleSubmit} />);
wrapper.find('button').simulate('click', mockParams);

Categories