React | this.props.function inside a function will not be called - javascript

I'm working on a Checklist with React and MaterialUI consisting out of two components, one that contains the data and another one that edits it, but there occurs the problem that the called function (handleDeleteChip()), which is passed on with props, will not be executed.
Parent Component:
export default class CheckList extends React.Component {
constructor() {
super();
this.state = {
taskData: {}
}
this.handleDeleteChip = this.handleDeleteChip.bind(this)
handleDeleteChip = (chipToDelete) => () => {
let obj = this.state.taskData
delete obj[chipToDelete]
this.setState({taskData: obj})
};
render() {
return (
<div>
{
<AddToDoComponent handleDeleteChip={this.handleDeleteChip}/>
}
</div>
Child Component:
class AddToDoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
chipData: props.taskData,
updateCondition: true
}
this.deleteHandler = this.deleteHandler.bind(this)
}
deleteHandler(chipToDelete){
this.props.handleDeleteChip(chipToDelete)
}

The definition of handleDeleteChip should be:
handleDeleteChip(chipToDelete) {
let obj = this.state.taskData
delete obj[chipToDelete]
this.setState({taskData: obj})
};
or if you are using a functional component
const handleDeleteChip = (chipToDelete) => {
....
};
Also, the brackets ({}) into the parent render are not needed.
Hope this helps you!

Try to bind this from the prop value.
export default class CheckList extends React.Component {
constructor() {
super();
this.state = {
taskData: {}
}
//Remove: this.handleDeleteChip = this.handleDeleteChip.bind(this)
handleDeleteChip = (chipToDelete) => () => {
let obj = this.state.taskData
delete obj[chipToDelete]
this.setState({taskData: obj})
};
render() {
return (
<div>
{
<AddToDoComponent handleDeleteChip={this.handleDeleteChip.bind(this)}/>
}
</div>

Related

How to call child's method from parent without using Refs?

Let's say I've a parent component A and a child B:
A:
class A {
constructor() {
this.state = {data: []};
}
handleClick = () => {
// api call
// set data state to the returned value from api
// call B's createTable method
}
render() {
return(
<div>
<button onClick={()=> this.handleClick()}>Fetch data</button>
<B data={this.state.data} />
</div>
}
}
B:
class B {
constructor() {
this.state = {...};
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
I want to call createTable method from A without using Refs.
What I've done so far is using componentDidUpdate life cycle method in B to check if data prop has changed or not, If it changed call createTable method but I want to know is this right? or there's a better way of doing it because I feel it is kinda hacky or maybe bad design.
class B {
constructor() {
this.state = {...};
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
this.createTable();
}
}
createTable = () => {
const { data } = this.props;
// do smth
}
render() {
return(...);
}
}
NOTE I don't want to use hooks either just class based component.
The following example might be useful
class Parent extends Component {
render() {
return (
<div>
<Child setClick={click => this.clickChild = click}/>
<button onClick={() => this.clickChild()}>Click</button>
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.getAlert = this.getAlert.bind(this);
}
componentDidMount() {
this.props.setClick(this.getAlert);
}
getAlert() {
alert('clicked');
}
render() {
return (
<h1 ref="hello">Hello</h1>
);
}
}

how to call multiple methods in onClick in react?

I have two components (Parent component & Child component) in my react app. I have two button clicks in my child component and I need to pass two props to the parent component. I use the code as follows.
The problem is, I can't include both methods in the parent component's element, but I need to. How can I use both edituser and deleteuser functions in the parent component?
Child component:
class EnhancedTable extends React.Component {
constructor(props){
super(props);
this.state = {
userID: 10
};
this.editByUserId = this.sendUserId.bind(this);
this.DeleteByUserId = this.sendUserId.bind(this);
}
editByUserId() {
this.props.onClick(this.state.userID);
}
DeleteByUserId() {
this.props.onClick(this.state.userID);
}
render() {
return (
<button onClick={this.sendUserId}>
<BorderColorIcon onClick={this.editUserById} className="action margin-r" />
<DeleteIcon onClick={this.deleteUserById} className="action margin-r" />
</button>
)
}
}
Parent component:
Import EnhancedTable from './EnhancedTable';
class Users extends Component {
constructor(props) {
super(props);
this.state = {
userID: null
};
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
editUser(idd) {
this.setState({
userID : idd
})
console.log("User Edited");
}
deleteUser(idd) {
this.setState({
userID : idd
})
console.log("User Deleted");
}
render() {
return(
<EnhancedTable onClick = {(e)=>{this.editUser; this.deleteUser;}}/>
)
}
}
You missed your ()
<EnhancedTable onClick = {(e)=>{this.editUser(); this.deleteUser();}}/>
You are doing it right in
<EnhancedTable onClick = {(e)=>{this.editUser; this.deleteUser;}}/>
A minor change is needed:
<EnhancedTable onClick = {(e)=>{this.editUser(e); this.deleteUser(e);}}/>
A quick reference for what changed here:
let x = () => {
console.log('hello');
}
x; // This simply does nothing as it is just a reference to the function
x(); // This instead invokes the function

How to handle two similar components in react

I am very new to React. I have two components: TimePickerComponent and the TimeDurationPickerComponent.
The TimePickerComponent gets passed a TimeString(string) via props(only if initial data exists) and displays it like "08:00". Code:
class TimePickerComponent extends React.Component {
_placeholder;
_defaultTimeString;
_timeString;
_errorStatus;
_classes;
constructor({ placeholder, defaultTimeString, timeString, errorStatus, classes }) {
super();
this._placeholder = placeholder;
this._defaultTimeString = defaultTimeString;
this._timeString = timeString;
this._errorStatus = errorStatus;
this._classes = classes;
}
get Placeholder() {
return this._placeholder;
}
get DefaultTimeString() {
return this._defaultTimeString ? this._defaultTimeString : CONTROLS_CONSTANTS.DEFAULT_TIME_STRING;
}
get TimeString() {
return this._timeString;
}
get ErrorStatus() {
return this._errorStatus;
}
get Classes() {
return this._classes;
}
render() {
return <FormControl>
<TextField error={this.ErrorStatus}
label={this.Placeholder}
defaultValue={this.TimeString ? this.TimeString : this.DefaultTimeString}
className={this.Classes.layout}
type="time"
InputLabelProps={{
shrink: true
}}
/>
</FormControl>
}
}
TimePickerComponent.propTypes = {
placeholder: PropTypes.string.isRequired,
defaultTimeString: PropTypes.string,
timeString: PropTypes.string,
errorStatus: PropTypes.bool
}
export default withStyles(styles)(TimePickerComponent);
The TimeDurationPickerComponent gets passed a TimeInMinutes(number) via props. But the display is the same as of the TimePickerComponent("08:00"). Code:
class TimeDurationPickerComponent extends React.Component {
_placeholder;
_timeInMinutes;
_classes;
constructor({ placeholder, timeInMinutes, classes }) {
super();
this._placeholder = placeholder;
this._timeInMinutes = timeInMinutes;
this._classes = classes;
}
get Placeholder() {
return this._placeholder;
}
get TimeInMinutes() {
return this._timeInMinutes;
}
get Classes() {
return this._classes;
}
get TimeString() {
let timeFormat = CONTROLS_CONSTANTS.TIME_FORMATS.HOURS_MINUTES_COLON_SEPARATED;
let duration = moment.duration({ minutes: this.TimeInMinutes });
//https://github.com/moment/moment/issues/463
return moment.utc(duration.asMilliseconds()).format(timeFormat);
}
render() {
return <TimePickerComponent
placeholder={this.Placeholder}
timeString={this.TimeString}
classes={this.Classes}
/>
}
}
TimeDurationPickerComponent.propTypes = {
placeholder: PropTypes.string.isRequired,
timeInMinutes: PropTypes.number
}
export default TimeDurationPickerComponent;
To avoid code redundancy I reused my TimePickerComponent in the TimeDurationPickerComponent and just convert the TimeInMinutes in a TimeString and pass it down to the TimePickerComponent via props.
My question now: Is this a good practice how I solved this or should I use a HigherOrderComponent for that? Or should I use an inheritance approach for that? Which solution would be the best and why?
Thank you in advance.
What you've done here is probably fine. It could be done with a higher order component as well but a composition based approach like what you have won't have any performance issues and to be honest it's probably more readable than using an HOC.
On another note you should be using this.props and this.state to represent your class properties. They are build into React components and are what will cause your component to automatically re-render upon change.
It also makes your code significantly more concise so for example you could reduce your second component down to something like this:
class TimeDurationPickerComponent extends React.Component {
constructor(props) {
super(props);
}
createTimeString() {
let timeFormat = CONTROLS_CONSTANTS.TIME_FORMATS.HOURS_MINUTES_COLON_SEPARATED;
let duration = moment.duration({ minutes: this.props.TimeInMinutes });
//https://github.com/moment/moment/issues/463
return moment.utc(duration.asMilliseconds()).format(timeFormat);
}
render() {
return <TimePickerComponent
placeholder={this.props.Placeholder}
timeString={this.createTimeString()}
classes={this.props.Classes}
/>
}
}
Example of a component that uses flow:
// #flow
import React from 'react';
import './css/ToggleButton.css';
type Props = {
handleClick: Function;
label: string;
};
type LocalState = {
active: bool,
};
class ToggleButton extends React.Component<Props, LocalState> {
clickHandler: () => void;
constructor(props: Props) {
super(props);
this.state = {
active: true,
};
this.clickHandler = this.clickHandler.bind(this);
}
clickHandler() {
this.setState({ active: !this.state.active });
this.props.handleClick();
}
render() {
const buttonStyle = this.state.active ? 'toggle-btn-active' : 'toggle-btn-inactive';
return (
<button
className={`toggle-btn ${buttonStyle}`}
onClick={this.clickHandler}
>{this.props.label}
</button>
);
}
}
export default ToggleButton;

React converting getInitialState (ES5) to this.state (ES6) with a property and method

I have issue with converting my react code to new format. Can you help me.
Now I have
function getStateFromFlux() {
return {
...
};
}
const TasklistsPage = React.createClass({
getInitialState() {
return {
...getStateFromFlux(),
isCreatingTaskList: false
};
},
....
_onChange() {
this.setState(getStateFromFlux());
}
});
I want smth like this one. But I have an error from my api request "error: TypeError: Cannot read property 'map' of undefined
at TasklistsPage.render"
function getStateFromFlux() {
return {
taskLists: TaskListsStore.getTaskLists()
};
}
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
func: getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
....
render() {
...
return (
...
{
this.state.taskLists.map(list =>
<ListItem
key={list.id}
leftIcon={<FolderIcon />}
primaryText={list.name}
onClick={router.history.push.bind(null, `/lists/${list.id}`)}
/>
)
}
...
)
}
_onChange() {
this.setState({func:getStateFromFlux()});
}
}
I don't know what I'm doing wrong.
Thanks for your help.
Instead of
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
func: getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
Try doing
export default class TasklistsPage extends React.Component{
constructor(props) {
super(props);
this.state = {
...getStateFromFlux(),
isCreatingTaskList: false
};
this._onChange = this._onChange.bind(this);
}
In the first case, you simply assigned the return value of getStateFromFlux() to the key func. So your tasklist was under this.state.func.taskList. By doing a spread with the function, it will just place the contents of the object it created within the object you pass it into. You can alternatively just do the . operator on the return value from the getStateFromFlux() function.
In your onChange I would advise doing the same, you are assigning it to the variable named func in your code.
_onChange() {
this.setState({ ...getStateFromFlux() });
}

React component method this.state.myState undefined after returning from child

I am having a Parent component (BookApplication) and a child component (SearchBox) in React. The SearchBox has an input field, and should give the input back to the parent for handling the event. That is working fine, but when i am back in the parent component in the method handleSearch the this.state... is undefined.
TypeError: Cannot read property 'books' of undefined
But searchInput has the value it should have.
But i need the books from this.state.books again :/
I understand that in the method handleSearch i am working in it's scope, so this.... is the context of handleSearch... but how do i get the arguments of it's component the BookApplication again?
I am still learning javascript, and i thought this shouldnt be a problem, because a function can always use the variables of it's parent object?
class BookApplication extends React.Component {
constructor() {
super();
this.state = {books: []};
}
componentDidMount() {
$.get(PATH, function (result) {
this.setState({
books: result
});
}.bind(this));
}
handleSearch(searchInput) {
//Sort the books list
var sortedList = [];
this.state.books.map(
function (currentBook) {
currentBook.keys().forEach(
function (key, pos) {
if (key.contains(searchInput)) {
sortedList.push(currentBook)
}
}
)
}
);
}
render() {
return (
<div>
<SearchBox onSearch={this.handleSearch}/>
<div className="book-list">
{this.state.books.map(function (currentBook) {
return <Book book={currentBook} key={currentBook.id}/>;
}) }
</div>
</div>
);
}
Here also my SearchBox:
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.state = {searchFieldInput: ''};
this.handleSearchChange = this.handleSearchChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSearchChange(event) {
this.setState({searchFieldInput: event.target.value});
}
handleSubmit(e) {
//Prevent the browser's defeault action of submitting the form
e.preventDefault();
var searchFieldInput = this.state.searchFieldInput.trim();
//Call the passed callback function
this.props.onSearch({searchFieldInput: searchFieldInput});
}
render() {
return (
<div className="book-search">
<input
type="text"
value={this.state.searchFieldInput}
onChange={this.handleSearchChange}
placeholder="Search..."
className="search-bar"
/>
<button onClick={this.handleSubmit} className="search-button">Search</button>
</div>
);
}
}
If your question is how to get parent's context from child component, then try
class ParentComponent extends React.Component {
...
...
clickHandler(event){}
render(){
<ChildComponent parent={this}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props);
}
render(){
let parent = this.props.parent;
return <button onClick={parent.clickHandler}></button>
}
}
And you will get an error here
componentDidMount() {
$.get(PATH, function (result) {
this.setState({
books: result
});
}.bind(this));
}
Because this in the callback function not referred to your Component's context. You should keep component's context in variable
componentDidMount() {
let self = this;
$.get(PATH, function (result) {
self.setState({
books: result
});
}.bind(this));
}
Finally decision is
constructor(props) {
super(props);
this.state = {books: []};
//add the following line into your code
this.handleSearch = this.handleSearch.bind(this);
}

Categories

Resources