react native - undefined is not a function updateState() - reactjs

I'm passing a function from Parent to Children components using FlatList but for some reason, the error occurs that undefined is not a function referring to updateState function.
Here's the parent component:
class Home extends Component {
/*
data items here
*/
updateState(data) {
this.setState({ data: data });
}
renderItem() {
return (
<ItemView updateParentState={ (data) => this.updateState(data) } />
)
}
render() {
return (
<FlatList
style={styles.fragmentContainer}
data={this.state.items}
keyExtractor={(item, index) => index.toString()}
renderItem={this.renderItem} />
)
}
}
Here's the child view:
class ItemView extends Component {
constructor(props) {
super(props);
this.state = {
data: "some data"
};
}
render() {
return (
<TouchableOpacity onPress={ () => this.props.updateParentState(this.state.data) }>
<Text>Item name here</Text>
</TouchableOpacity>
)
}
}

This should fix your problem:
render() {
return (
<FlatList
style={styles.fragmentContainer}
data={this.state.items}
keyExtractor={(item, index) => index.toString()}
renderItem={this.renderItem.bind(this)} />
)
}
You should always use the .bind(this) function if you're going to pass a function as props so that this does not get lost. Or you could use the arrow function if you're not a fan of .bind.
Ex:
{() => this.renderItem()}

Related

React component not running componentDidmount in my project, so I get error isEdit of undefined?

When I run my react-native component, I get the following error:
However, in the debugger in constructor and componentWillmount and render lifecycle, isEdit is false.
Only componentDidmount is not printing anything, so I think the component is not running componentDidmount lifecycle and I don't know the error reason for this.
constructor(props: IProps) {
// isEdit: false
super(props);
// const {navigation} = this.props;
props.navigation.setOptions({
headerRight: () => {
return <HeaderRightBtn onSubmit={this.onSubmit} />;
},
});
}
componentWillmount() {
// isEdit: false
}
componentDidmount() {
// isEdit of undefined
}
render() {
// isEdit: false
const {categorys, isEdit} = this.props;
const {myCategorys} = this.state;
const classifyGroup = _.groupBy(categorys, (item) => item.classify);
return (
<ScrollView style={styles.container}>
<Text style={styles.classifyName}>我的分类</Text>
<View style={styles.classifyView}>
<DragSortableView
dataSource={myCategorys}
renderItem={this.renderItem}
sortable={isEdit}
fixedItems={fixedItems}
keyExtractor={(item) => item.id}
onDataChange={this.onDataChange}
parentWidth={parentWidth}
childrenWidth={itemWidth}
childrenHeight={itemHeight}
marginChildrenTop={margin}
onClickItem={this.onClick}
/>
{/* {myCategorys.map(this.renderItem)} */}
</View>
<View>
{Object.keys(classifyGroup).map((classify) => {
return (
<View key={classify}>
<Text style={styles.classifyName}>{classify}</Text>
<View style={styles.classifyView}>
{classifyGroup[classify].map((item, index) => {
if (
myCategorys.find(
(selectedItem) => selectedItem.id === item.id,
)
) {
return null;
}
return this.renderUnSelectedItem(item, index);
})}
</View>
</View>
);
})}
</View>
</ScrollView>
);
}
}
Correct names of methods are
“componentDidMount()” and “UNSAFE_componentWillMount()” as per componentdidmount and unsafe_componentwillmount respectively.
Note that componentWillMount() is changed to UNSAFE_componentWillMount() as per the official docs.

How to Call a Function inside a Render Class in React-native

I want to call a function inside an imported render class. I tried the following but no success.
class1.js
import class2 from "./class2";
export default class1 MainCategoriesScreen extends React.PureComponent {
renderItem({ item }) {
return <Class2 product={item}/>
}
changeCategoryId(id){
console.log(id);
}
render() {
return (
<FlatList
data={this.state.products}
renderItem={this.renderItem}
...
}
and for class2
render() {
return (
<Card style={{flex:1}}>
<CardItem cardBody button
onPress={this.changeCategoryId(product.id)}>
...
</CardItem>
...
}
export default withNavigation(class2 );
Also I tried these:
this.changeCategoryId(product.id)
this.changeCategoryId(product.id)
this.changeCategoryId.bind(product.id)
this.props.changeCategoryId(product.id)
You can pass the changeCategoryId method from class 1 to class 2 as a prop, and then call it like this.props.changeCategoryId():
// class 1
constructor(props) {
super(props);
...
this.renderItem = this.renderItem.bind(this);
this.changeCategoryId = this.changeCategoryId.bind(this);
}
renderItem({ item }) {
return <Class2 product={item}
changeCategoryId={this.changeCategoryId}/>
}
// class 2
render() {
return (
<Card style={{flex:1}}>
<CardItem cardBody button
onPress={this.props.changeCategoryId(product.id)}>
...
</CardItem>
...
Note that you need to bind both changeCategoryId and renderItem in class 1.
I recently had the same issue, just do this:
export default class1 MainCategoriesScreen extends React.PureComponent {
renderItem = ({ item }) => {
return <Class2 product={item} onPress={this.myfunction} />
}
myfunction = (id) => {
console.log(id);
}
render() {
return (
<FlatList
data={this.state.products}
renderItem={this.renderItem}
...
and
render() {
return (
<Card style={{flex:1}}>
<CardItem cardBody button
onPress={() => this.props.onPress(product.id)}>
...
</CardItem>
...
You can also try this:
renderItem = ({ item }) => {
return <Class2 product={item} onPress={id => this.myfunction(id)} />
}
myfunction(id) {
console.log(id);
}
renderItem({ item }) {
return <Class2 product={item}/>
}
You appear to be missing passing the prop into Class2, which will handle the changeCategoryId.
renderItem({ item }) {
return <Class2 changeCategoryId={this.changeCategoryId} product={item}/>
}
This means, Class2 will now have access to a prop, called changeCategoryId which will be Class1's changeCategoryId function.
Then in the render function within your Class2, you can do:
<CardItem cardBody button
onPress={() => this.props.changeCategoryId(product.id)}>
...

Sending data from Child to Parent React

I have subdivided my components and I want to change state of text using deleteName function from child component. However I have used onPress={this.props.delete(i)} in my child component which is not working. The error that occurs for me is:
undefined variable "I"
Here is my code:
App.js
export default class App extends Component {
state = {
placeName: '',
text: [],
}
changeName = (value) => {
this.setState({
placeName: value
})
}
deleteName = (index) => {
this.setState(prevState => {
return {
text: prevState.text.filter((place, i) => {
return i!== index
})
}
}
}
addText = () => {
if (this.state.placeName.trim === "") {
return;
} else {
this.setState(prevState => {
return {
text: prevState.text.concat(prevState.placeName)
};
})
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.inputContainer}>
<Input changeName={this.changeName}
value={this.state.placeName} />
<Button title="Send" style={styles.inputButton}
onPress={this.addText} />
</View>
<ListItems text={this.state.text} delete={this.deleteName}/>
{/* <View style={styles.listContainer}>{Display}</View> */}
</View>
);
}
}
and child component ListItems.js
const ListItems = (props) => (
<View style={styles.listitems}>
<Text>{this.props.text.map((placeOutput, i) => {
return (
<TouchableWithoutFeedback
key={i}
onPress={this.props.delete(i)}>
onPress={this.props.delete}
<ListItems placeName={placeOutput}/>
</TouchableWithoutFeedback>
)
})}
</Text>
</View>
);
You need to bind the index value at the point of passing the props to the child.
delete = index => ev => {
// Delete logic here
}
And in the render function, you can pass it as
items.map((item, index) => {
<ChildComponent key={index} delete={this.delete(index)} />
})
In your child component, you can use this prop as
<button onClick={this.props.delete}>Click me</button>
I have created a Sandbox link for your reference
Instead of onPress={this.props.delete(i)}, use onPress={() => this.props.delete(i)}
In order to have the cleaner code, you can use a renderContent and map with }, this);like below. Also you need to use: ()=>this.props.delete(i) instead of this.props.delete(i) for your onPress.
renderContent=(that)=>{
return props.text.map((placeOutput ,i) => {
return (
<TouchableWithoutFeedback key={i} onPress={()=>this.props.delete(i)}>
onPress={this.props.delete}
</TouchableWithoutFeedback>
);
}, this);
}
}
Then inside your render in JSX use the following code to call it:
{this.renderContent(this)}
Done! I hope I could help :)

this.props undefined in React native: TypeError: Cannot read property 'title' of undefined

I am trying to create a component that is a flatlist and to use probs to pass down some information to the component in the _renderItem function:
class ArticleList extends React.PureComponent {
_renderItem = ({ item }) => (
<DummyText title={item.title} />
);
render() {
return (
<View>
<FlatList
data={articles}
renderItem={this._renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
export default ArticleList;
This is the component for the FlatList.
The DummyText Component is very simple and just for trial purposes:
class DummyText extends React.Component {
render() {
console.log(this.props);
return (
<View>
<Text>{this.props.title}</Text>
</View>
);
}
}
export default DummyText;
However, when I do this, the this.probs part appears to be undefined and I get an error saying:
TypeError: Cannot read property 'title' of undefined
I have made sure that the item has an attribute title and it works fine if the _renderItemdirectly creates <View><Text>{item.title}</Text></View>.
Solution
Fix typo.
this.probs.title
=>
this.props.title
Why
Normally, undefined message say there is no value. Thus, you able to know easily what is wrong from title's parent value. And typo is happened often including me and it will be prevent by using linter as ESLint.
Actually, you are not passing item to your _renderItem function
render() {
return (
<View>
<FlatList
data={articles}
renderItem={this._renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
You have to pass item to your _renderItem function as,
render() {
return (
<View>
<FlatList
data={articles}
renderItem={(item) => this._renderItem(item)}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
Then same as you are using,
_renderItem = ({ item }) => (
<DummyText title={item.title} />
);

How to record other events in FlatList?

I am using FlatList to render items. Each item is a separate card style component. Each item has onPress event handler which changes the component.
Here is my Flatlist.
<FlatList
data={data}
renderItem={({ item }) => {
return <CardItem courseData={item} />
}}
ref={this.flatList}
keyExtractor={
(item) => { return item.content_address }
}
initialNumToRender={10}
showsVerticalScrollIndicator={false}
style={{ marginTop: 50 }}
/>
Here is the CardItem Component
constructor{
this.state = {change:false}
}
_onPress = () => {
this.setState({change: true})
}
render() {
if (this.state.change) {
return (//return changes)
} else {
return (
<TouchableOpacity
ref="myRef"
activeOpacity={0.5}
onPress={this._onPress}>
...
</TouchableOpacity>
)
}
}
Now what I want is to have only one card component changed at a time.
So when a user touches on 1st card component, it should change. But when a user touches 2nd card component, 1st should change back to the previous state and 2nd should change.
I saw FlatList documentation here but not sure which methods can help me?
If you store your toggled item in parent state you can check and render accordingly. Also storing toggled value in child state will cause a bug where if the item moves enough off to the screen it will be unmounted and the internal state of the child component will be reset. This would cause undesired toggle in your list. Storing state in parent component will help to overcome this issue.
Example
class App extends Component {
constructor() {
this.state = { toggledItem: null }
}
onPressItem = (itemId) => {
this.setState({toggledItem: itemId})
}
render() {
<FlatList
data={data}
renderItem={({ item }) => {
return <CardItem
courseData={item}
onPress={this.onPressItem}
toggeled={item.id === this.state.toggledItem}
/>
}}
ref={this.flatList}
keyExtractor={
(item) => { return item.content_address }
}
initialNumToRender={10}
showsVerticalScrollIndicator={false}
style={{ marginTop: 50 }}
/>
}
}
class CardItem extends Component {
_onPress = () => {
this.props.onPress(this.props.courseData.id)
}
render() {
if (this.props.toggeled) {
return (//return changes)
} else {
return (
<TouchableOpacity
ref="myRef"
activeOpacity={0.5}
onPress={this._onPress}>
...
</TouchableOpacity>
)
}
}
}

Resources