I got an error when trying to call a function declared inside a component. here is the error message: Uncaught TypeError: Cannot read property 'handleClick' of undefined
class LanguageDropDown extends Component {
constructor(props) {
super(props);
this.state = {languageValue: ""};
this.handleLanguageClick = this.handleLanguageClick.bind(this);
}
handleLanguageClick = (languageKey) => {
this.setState({ languageValue: languageKey});
}
render() {
const {intl, value, onChange, onFocus, onBlur, onClick} = this.props;
return (
<React.Fragment>
{
map(languageWhitelist, function(Key) {
return (
<option onClick={this.handleLanguageClick(Key)}
key={Key} value={Key}></option>
);
}, this)
}
</React.Fragment>
);
}
}
This should work
class LanguageDropDown extends Component {
state = { languageValue: '' };
_handleLanguageClick(languageValue) {
this.setState({ languageValue });
};
render() {
const { intl, value, onChange, onFocus, onBlur, onClick } = this.props;
return (
<React.Fragment>
{languageWhitelist.map(language => (
<option
onClick={() => this._handleLanguageClick(language)}
key={language}
>
{language}
</option>
))}
</React.Fragment>
);
}
}
By using arrow functions inside a class,it becomes automatically bound to the class,
so just remove the binding statement from the constructor.
And also set the onClick props to a callback function.
class LanguageDropDown extends Component {
constructor(props) {
super(props);
this.state = { languageValue: '' };
}
_handleLanguageClick = languageValue => {
this.setState({ languageValue });
};
render() {
const { intl, value, onChange, onFocus, onBlur, onClick } = this.props;
return (
<React.Fragment>
{languageWhitelist.map(language => (
<option
onClick={() => this._handleLanguageClick(language)}
key={language}
>
{language}
</option>
))}
</React.Fragment>
);
}
}
Related
I'm just starting out with React, adapting the tic tac toe tutorial for my case.
I'm trying to click on the grandchild component to change the state of the grandparent component . Code is as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: [
{
id: 1,
show: false
},
{
id: 2,
show: false
}
]
}
}
handleClick(i) {
const fields = this.state.fields.slice();
fields[i].show = true;
this.setState({fields: fields});
}
render() {return <Preview />}
}
const Preview = (props) => {
return (
<div className="preview">
{props.fields.map((field) => (
<Field data={field} key={field.id} onClick={ props.onClick(field.id) }/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};
I get a TypeError: Cannot read property 'state' of undefined from this line:
handleClick(i) {
const fields = this.state.fields.slice();
Issues
this of the App class isn't bound to the handleClick function. This is cause of TypeError: Cannot read property 'state' of undefined error.
You are mutating your state object. Slicing the array creates a new array reference, but fields[i].show = true; mutates the object reference in state.
You don't pass fields or onClick props to Preview.
The onClick callback isn't called correctly in Preview.
Solution
Bind this to the handler or convert to arrow function so it is automatically bound.
constructor(props){
...
this.handleClick = this.handleClick.bind(this);
}
or
handleClick = (i) => { ..... };
DON'T MUTATE STATE. Shallow copy state then update properties.
handleClick = (id) => {
this.setState(prevState => ({
fields: prevState.fields.map((field) => {
return field.id === id ? {
...field,
show: true,
} : field;
}),
}));
};
Pass fields and handleClick as onClick to Preview.
render() {
return (
<Preview
fields={this.state.fields}
onClick={this.handleClick}
/>
);
}
Call props.onClick correctly with the id.
{props.fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => props.onClick(field.id)}
/>
))}
I've added some explanations, check the comments
// [...]
render() {
// Here you need to pass "fields" and "handleClick" as props:
return <Preview fields={this.state.fields} onClickField={this.handleClick} />
}
}
const Preview = (props) => {
// Here you get the props:
const { fields, onClickField } = props;
// Your onclick was a function call instead of just a function
return (
<div className="preview">
{fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => onClickField(field.id) }
/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};
I've tried to console.log the value of the text input but I get the error "undefined is not an object (evaluating 'this.state.inputValue')". What's the problem? Thank you!
class SearchScreen extends React.Component {
state = {
inputValue: "",
};
search() {
console.log(this.state.inputValue);
}
render() {
return (
<View>
<TextInput
onChangeText={
((inputValue) => this.setState({ inputValue }),
this.search)
}
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
The problem is in the way you've implemented it. Please try as below...
class SearchScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
};
}
search() {
console.log(this.state.inputValue);
}
render() {
return (
<View>
<TextInput
onChangeText={(inputValue) => {
this.setState({ inputValue });
this.search();
}}
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
This problem occurred because two things.
First:
The this.setState is a async function.
If you pass a function after the setState this will work like a .then() in a promisse.
Second:
If you pass one function after another separating them by ',' the rightmost function will be executed first
You can resolve this doing something like that:
onChange={ inputValue => {
this.setState({ inputValue });
this.search();
}}
Or you can try something like that:
class SearchScreen extends React.Component {
state = {
inputValue: "",
};
search = () {
console.log(this.state.inputValue);
}
setSearch = inputValue => {
// The function 'search' will be execute after the state was set
this.setState(
{ inputValue },
() => this.search()
);
}
render() {
return (
<View>
<TextInput
onChangeText={ inputValue => this.setSearch(inputValue) }
value={this.state.inputValue}
/>
</View>
);
}
}
export default SearchScreen;
You didn't set the value of state property. provide a value to setState.
this.setState({property : value})
The problem is this line:
onChangeText={
((inputValue) => this.setState({ inputValue }),
this.search)
}
You can use the short function notation only when your function has one statement:
(inputValue) => this.setState({ inputValue })
You actualy have 2 statements though, so you need to create a full function block using {}
(inputValue) => {
this.setState({ inputValue })
this.search()
}
I have a class component, within that calling a function. I have a state variable and want to update the state in function. Since it is different function I'm not able to update the value. How can I get the selected items details and update the state? when I do setState, receiving following error as 'TypeError: this.setState is not a function'
any help appreciated
Component
import React, { Component } from 'react'
import PropTypes from "prop-types";
import statedist from "./StateDistrict.json";
const suggestions = statedist.states;
function DownshiftMultiple(props) {
const { classes } = props;
const [inputValue, setInputValue] = React.useState("");
const [selectedItem, setSelectedItem] = React.useState([]);
function handleKeyDown(event) {
if (
selectedItem.length &&
!inputValue.length &&
event.key === "Backspace"
) {
setSelectedItem(selectedItem.slice(0, selectedItem.length - 1));
}
}
function handleInputChange(event) {
setInputValue(event.target.value);
}
function handleChange(item) {
let newSelectedItem = [...selectedItem];
if (newSelectedItem.indexOf(item) === -1) {
newSelectedItem = [...newSelectedItem, item];
}
setInputValue("");
setSelectedItem(newSelectedItem);
this.setState({ SelectedState: newSelectedItem }); // here i want to update selected items
}
const handleDelete = item => () => {
const newSelectedItem = [...selectedItem];
newSelectedItem.splice(newSelectedItem.indexOf(item), 1);
setSelectedItem(newSelectedItem);
};
return (
<Downshift
id="downshift-multiple"
inputValue={inputValue}
onChange={handleChange}
selectedItem={selectedItem}
>
{({
getInputProps,
getItemProps,
getLabelProps,
isOpen,
inputValue: inputValue2,
selectedItem: selectedItem2,
highlightedIndex
}) => {
const { onBlur, onChange, onFocus, ...inputProps } = getInputProps({
onKeyDown: handleKeyDown,
// placeholder: "Select multiple State"
});
return (
<div className={classes.container}>
{renderInput({
fullWidth: true,
classes,
// label: "States",
InputLabelProps: getLabelProps(),
InputProps: {
startAdornment: selectedItem.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
className={classes.chip}
onDelete={handleDelete(item)}
/>
)),
onBlur,
onChange: event => {
handleInputChange(event);
onChange(event);
},
onFocus
},
inputProps
})}
{isOpen ? (
<Paper className={classes.paper} square>
{getSuggestions(inputValue2).map((suggestion, index) =>
renderSuggestion({
suggestion,
index,
itemProps: getItemProps({ item: suggestion.state }),
highlightedIndex,
selectedItem: selectedItem2
})
)}
</Paper>
) : null}
</div>
);
}}
</Downshift>
);
}
class autoCompleteState extends Component {
constructor(props) {
super(props);
this.state = {
SelectedState:'',
}
// this.showProfile = this.showProfile.bind(this)
}
render() {
const { classes, } = this.props;
return (
<div>
<DownshiftMultiple classes={classes} />
</div>
)
}
}
export default withStyles(Styles)(autoCompleteState);
You can't and shouldn't access the context (this) of other components directly to update its state, especially not with a functional component.
What you have to do is pass a function as a prop to your DownshiftMultiple component which itself gets the value with which you want to update the state.
function DownshiftMultiple(props) {
/* ... */
function handleChange(item) {
let newSelectedItem = [...selectedItem];
if (newSelectedItem.indexOf(item) === -1) {
newSelectedItem = [...newSelectedItem, item];
}
setInputValue("");
setSelectedItem(newSelectedItem);
this.props.onChange(newSelectedItem); // Use the new function prop
}
/* ... */
}
class autoCompleteState extends Component {
/* ... */
onDMChange = (newSelectedItem) => this.setState({ SelectedState: newSelectedItem });
render() {
const { classes, } = this.props;
return (
<div>
<DownshiftMultiple classes={classes} onChange={this.onChange} />
</div>
)
}
}
Also on a sidenote I would recommend to encapsulate your event handling functions inside your functional DownshiftMultiple component with the useCallback hook. Something like const newSelectedItem = [...selectedItem]; would always use the value that the state has been initialised with without a hook.
// For example your handle delete
const handleDelete = React.useCallback(item => () => {
const newSelectedItem = [...selectedItem];
newSelectedItem.splice(newSelectedItem.indexOf(item), 1);
setSelectedItem(newSelectedItem);
}, [selectedItem]);
You pass on a handler to the child component, which it will invoke with the value to update and the update action happens in the parent
import React, { Component } from 'react'
import PropTypes from "prop-types";
import statedist from "./StateDistrict.json";
const suggestions = statedist.states;
function DownshiftMultiple(props) {
const { classes } = props;
const [inputValue, setInputValue] = React.useState("");
const [selectedItem, setSelectedItem] = React.useState([]);
function handleKeyDown(event) {
if (
selectedItem.length &&
!inputValue.length &&
event.key === "Backspace"
) {
setSelectedItem(selectedItem.slice(0, selectedItem.length - 1));
}
}
function handleInputChange(event) {
setInputValue(event.target.value);
}
function handleChange(item) {
let newSelectedItem = [...selectedItem];
if (newSelectedItem.indexOf(item) === -1) {
newSelectedItem = [...newSelectedItem, item];
}
setInputValue("");
setSelectedItem(newSelectedItem);
props.setSelectedState(newSelectedItem);
}
const handleDelete = item => () => {
const newSelectedItem = [...selectedItem];
newSelectedItem.splice(newSelectedItem.indexOf(item), 1);
setSelectedItem(newSelectedItem);
};
return (
<Downshift
id="downshift-multiple"
inputValue={inputValue}
onChange={handleChange}
selectedItem={selectedItem}
>
{({
getInputProps,
getItemProps,
getLabelProps,
isOpen,
inputValue: inputValue2,
selectedItem: selectedItem2,
highlightedIndex
}) => {
const { onBlur, onChange, onFocus, ...inputProps } = getInputProps({
onKeyDown: handleKeyDown,
// placeholder: "Select multiple State"
});
return (
<div className={classes.container}>
{renderInput({
fullWidth: true,
classes,
// label: "States",
InputLabelProps: getLabelProps(),
InputProps: {
startAdornment: selectedItem.map(item => (
<Chip
key={item}
tabIndex={-1}
label={item}
className={classes.chip}
onDelete={handleDelete(item)}
/>
)),
onBlur,
onChange: event => {
handleInputChange(event);
onChange(event);
},
onFocus
},
inputProps
})}
{isOpen ? (
<Paper className={classes.paper} square>
{getSuggestions(inputValue2).map((suggestion, index) =>
renderSuggestion({
suggestion,
index,
itemProps: getItemProps({ item: suggestion.state }),
highlightedIndex,
selectedItem: selectedItem2
})
)}
</Paper>
) : null}
</div>
);
}}
</Downshift>
);
}
class autoCompleteState extends Component {
constructor(props) {
super(props);
this.state = {
SelectedState:'',
}
// this.showProfile = this.showProfile.bind(this)
}
setSelectedState = (newState) => {
this.setState({ SelectedState: newState });
}
render() {
const { classes, } = this.props;
return (
<div>
<DownshiftMultiple classes={classes} setSelectedState={this.setSelectedState}/>
</div>
)
}
}
export default withStyles(Styles)(autoCompleteState);
I've been using React native for a month now but it's my first time to use a CheckBox in my application. So, lately I've been struggling to check a specific checkbox inside a Flatlist but now I can.
But upon testing my checkboxs I did notice that once I check a specific a CheckBox(or more than 1 checkbox) it doesn't UNCHECK.
So, my goal is to make a CheckBox that can check(ofcourse) and also uncheck, if ever a user mischeck or mistap a CheckBox.
Here's my code
export default class tables extends Component {
constructor(props){
super(props)
this.state = {
...
check: false
}
}
checkBox_Test = (item, index) => {
let { check } = this.state;
check[index] = !check[index];
this.setState({ check: check })
alert("now the value is " + !this.state.check);
alert("now the value is " + item.tbl_id);
console.log(item.tbl_id)
}
render() {
return(
<View>
....
<Flatlist
....
<CheckBox
value = { this.state.check }
onChange = {() => this.checkBox_Test(item) }
/>
....
/>
<View/>
)
}
}
Method 1: Make check an object
export default class tables extends Component {
constructor(props){
super(props)
this.state = {
...
check: {}
}
}
checkBox_Test = (id) => {
const checkCopy = {...this.state.check}
if (checkCopy[id]) checkCopy[id] = false;
else checkCopy[id] = true;
this.setState({ check: checkCopy });
}
render() {
return(
<View>
....
<Flatlist
....
<CheckBox
value = { this.state.check[item.tbl_id] }
onChange = {() => this.checkBox_Test(item.tbl_id) }
/>
....
/>
<View/>
)
}
}
Method 2: Make a separate item for each FlatList item
class ListItem extends Component {
constructor(props){
super(props)
this.state = {
...
check: false
}
}
checkBox_Test = (id) => {
this.setState((prevState) => ({ check: !prevState.check }));
}
render() {
return(
<View>
<CheckBox
value = { this.state.check }
onChange = { this.checkBox_Test }
/>
</View>
)
}
}
Let me know if it works for you
I'm having a couple of questions with my React dropdown component. Basically, I want to get the selected object from the dropdown back to the Parent component. Right now the dropdown list is working and I seem to be able to get back a {this.state.selectedUser} to the parent from the Dropdown component. The only thing that isn't working is the default value.
From the parent, I want to pass some details from the object selected from Dropdown to a sister component, UserIcon.
First I have my dropdown which has an onChange. I was attempting to add a defaultValue to the Select to fix the issue of no default but no dice so far.
import React from 'react';
class PolicyDropdown extends React.Component {
render() {
let initialUsers = this.props.state.users;
let alphabetizeUsers = initialUsers
.sort((a, b) => {
return a.name > b.name;
})
.map(obj => {
return (
<option key={obj.id} value={obj.name}>
{obj.name}
</option>
);
});
return (
<select
onChange={e => this.props.onChange(e.target.value)}
>
{alphabetizeUsers}
</select>
);
}
}
export default UserDropdown;
Then I have a middle component that makes the api call .
import UserDropdown from './UserDropdown';
class UserHeader extends React.Component {
state = {
users: []
};
componentDidMount() {
let initialUsers = [];
fetch('http://localhost:3000/users')
.then(response => {
return response.json();
})
.then(data => {
this.setState({ users: data });
});
}
render() {
return (
<UserDropdown state={this.state} onChange={this.props.onChangePolicy} />
);
}
}
export default UserHeader;
And finally the main component where I should be handling the data, and passing the information from the dropdown into a new component, UserIcon.
class Main extends Component {
state = {
selectedUser: this.props.user
};
onChangeUser = user => {
this.setState({ selectedUser: user });
};
render() {
return (
<div className="App">
<header className="App-header">
<span className="App-title">
SELECT A POLICY:{' '}
<UserHeader onChangeUser={this.onChangeUser} />
</span>
<br /> <br />
<UserIcon onChangeUser={this.onChangeUser} />
Dropdown value here: {this.state.selectedUser}
</header>
</div>
);
}
}
export default Main;
Where the value is displayed as this.state.selectedUser. When I changed the value to just obj, I tried doing this.state.selectedUser.name but the console complained Uncaught TypeError: Cannot read property 'name' of undefined
. I want to pass the id as a prop to UserIcon component for it to use as a variable.
You have to pass the user object back to Main via a callback. This is a simplified version of what that would look like:
const UserIcon = props => <div>{props.user.name}</div>;
class UserDropdown extends React.Component {
render() {
let initialUsers = this.props.state.users;
let alphabetizeUsers = initialUsers
.sort((a, b) => {
return a.name > b.name;
})
.map(obj => {
return (
<option key={obj.id} value={obj.id}>
{obj.name}
</option>
);
});
return (
<select
onChange={e => this.props.onChange(e.target.value)}
>
{alphabetizeUsers}
</select>
);
}
}
class UserHeader extends React.Component {
state = {
users: [
{name: "thompson", id: 1},
{name: "anderson", id: 2}
]
};
handleChange = selectedId => {
const selectedUser = this.state.users.find( el => el.id === selectedId);
this.props.onChange(selectedUser);
}
render() {
return (
<UserDropdown state={this.state} onChange={this.handleChange} />
);
}
}
class App extends React.Component {
state = {
selectedUser: {
name: "",
id: ""
}
};
onChangeUser = user => {
this.setState({ selectedUser: user });
};
render() {
return (
<div className="App">
<header className="App-header">
<span className="App-title">
SELECT A POLICY:{' '}
<UserHeader onChange={this.onChangeUser} />
</span>
<br /> <br />
<UserIcon user={this.state.selectedUser} />
Dropdown value here: {JSON.stringify(this.state)}
</header>
</div>
);
}
}