I'm developing in React Native.
I have a FlatList that renders items. I have added TouchableOpacity and would like to call a function upon press of that item, but the function is not being called.
When I click on the item I get an error of cannot find variable: _onPress
I think it's an issue with scoping. Would someone be able to explain to me what is going wrong please?
I guess a secondary question is: will my _onPress console.log the item name by passing the prop in like I have?
export default class ModalScreen extends React.Component {
//..
_onPress = (item) => {
console.log('Clicked:' + item)
};
renderItem({ item }) {
return (
<TouchableOpacity onPress={() => this._onPress(item)}>
<View>
<Text>{item.name}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
//..
<View style={{flex: 2, backgroundColor: '#FFF', flexDirection:'row'}} >
<FlatList
data={this.state.searchedItems}
renderItem={this.renderItem}
/>
</View>
//..
try to change this 'renderItem({ item }) {' with this 'renderItem = ({ item }) => {
Related
I'm trying to make a wrapper component in react-native that I can pass down all its props to the children it wraps around. What I really want is to pass down all function props down to all its children. It looks something like this below. I want the onPress in Wrapper to be called when the TouchableOpacity is pressed.
I tried this below but it doesn't work
const Wrapper = ({children,...props})=>{
return <View {...props}>{children}</View>
}
const App = ()=>{
return (
<View style={{flex:1}}>
<Wrapper onPress={()=>{console.log(2)}}>
<TouchableOpacity/>
</Wrapper>
</View>
)
}
It looks like you're looking to map the children and apply the props to each one. That might look like this:
const Wrapper = ({children,...props})=>{
return (<>
{React.Children.map(children, child => (
React.cloneElement(child, {...props})
))}
</>);
}
(method of mapping the children borrowed from this answer)
const App = () => {
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
// do the action need here here
}}>
<Wrapper >
</Wrapper>
</TouchableOpacity>
</View>
)
}
I would advise you to use hooks function instead
If you try to reuse functions that are related
** useAdd.js **
export default () => {
const addFuction(a, b) => {
// do preprocessing here
return a + b
}
return [addFuction]
}
main componet
import useAdd from "/useAdd";
const App = () => {
const [addFuction] = useAdd()
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
addFuction(4,5)
}}>
...action component...
</TouchableOpacity>
</View>
)
}
console in useAdd hook.... to see visual changes use the react useState
I have scroll view in my HomeScreen with my measures that is array of Measure component.
<ScrollView>{measures}</ScrollView>
My Measure component looks like this:
class Measure extends Component {
render() {
return (
<View key={this.props.keyval}>
<TouchableOpacity onPress={deleteItem(this.props.val.measure_id)}>
<Text style={styles.measure}>
ID: {this.props.val.measure_id}
</Text>
</TouchableOpacity>
</View>
);
}
deleteItem(id) {
// delete item
);
}
}
My question is, how to notify parent component HomeScreen that Measure was deleted to reload scroll view items? Or Maybe you have better idea how to:
display measures
delete one in onPress item
reload items in scroll view
Thans for any advices
In your case it should be something like this:
class HomeScreen extends Component {
state = {
measures: []
};
handleDelete = (id) => {
// item was deleted
}
render() {
const { measures } = this.state;
const measuresList = measures.map(measure => (
<Measure
key={measure.measure_id}
onDelete={this.handleDelete}
val={measure}
/>
));
return (
<ScrollView>{measuresList}</ScrollView>
);
}
}
class Measure extends Component {
render() {
return (
<View key={this.props.keyval}>
<TouchableOpacity onPress={deleteItem(this.props.val.measure_id)}>
<Text style={styles.measure}>
ID: {this.props.val.measure_id}
</Text>
</TouchableOpacity>
</View>
);
}
deleteItem(id) {
const { onDelete } = this.props;
// delete item
onDelete(id); //will call parent method.
}
}
I recommend using FlatList as it render only those items which are visible. Much better in terms of performance, especially in big lists taken from API.
Example:
<FlatList
data={measures}
keyExtractor={item => item.id}
renderItem={({ item }) => <Measure
id={item.id}
anyOtherNeededPropsKey={anyOtherNeededPropsValue}
/>}
/>
Pass an additional property onDelete to the Measure and call it in deleteItem method
I am still having trouble understanding ref's in React Native (and React in general). I am using functional component. I have a FlatList that has many items. How do I create a reference for a thing within an item like a Text or View component?
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => 'text' + item.id = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
Then in _editItem I want to reference the Text component so that I can change its text from 'EDIT' to 'EDITING', or even change its style, or whatever.
_editPost = id => {
console.log(text + id)
}
I have tried...
FeedComponent = () => {
let editPosts = {}
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => editPosts[item.id] = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
...and a few other things, but I think I might be way off so I could use some guidance.
Typically you don't use refs in react to update content like text. Content should be rendered based on the current props and state of your component.
In the case you describe you'll probably want to set some state in the parent component that then impacts the rendering of the item.
As a sidenote refs are used if you need to trigger a method on a child component like calling focus on a TextInput for example but not for imperatively updating component content.
In your case you'll want to update some state representing the current active item. Something like:
import React, {useState} from 'react';
FeedComponent = () => {
const [activeItem, setActiveItem] = useState(null);
<FlatList
data={data}
renderItem={({ item }} => {
return (
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => setActiveItem(item.id)}>
{activeItem === item.id
? <Text>EDITING</Text>
: <Text>EDIT</Text>
}
</TouchableOpacity>
</View>
);
}
extraData={activeItem}
/>
I am confused about how to properly dynamically add/create same components on button press for react native. I have used .map(()=>{}) on existing info to create components and then display the results.
Would I have to save each new component into a setstate array, then map that?
I looked a little into refs, but wasn't sure how that was better than just a setstate. The problem I see is if I want to update the value for each component, how would I go about that if their all originally duplicates?
Something along the lines of this:
class SingleExercise extends Component {
constructor(props) {
super(props);
this.state = {
objInfo: this.props.navigation.getParam("obj"),
currentSetSelected: 0
};
this.addSet = this.addSet.bind(this);
}
addSet = () => {
return (
<addSet />
)
}
render() {
const { navigation } = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ height: 80 }}>
<addSet />
<View>
<Button //here
large={false}
onPress={() => {
this.addSet();
}}
title={"add more"}
/>
</View>
</View>
</View>
);
}
}
const addSet = () => {
return (
<TouchableHighlight>
<View>
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1}}
defaultValue={'test'}
onChangeText={(text) => this.setState({text})}
/>
</View>
</TouchableHighlight>
);
}
Here is what I would do:
Every click on addSet button should increment the AddSets counter like this:
<Button
large={false}
onPress={() => {
this.setState({addSetsCounter: this.state.addSetsCounter});
}}
title={"add more"}
/>
After every state update your component will be re-rendered. So now, all you need to do is to forLoop in through that counter and return as many AddSets components as needed. A simple for loop with .push() inside would do.
Inside render, before return place something like that:
let sets =[];
for(let i =0;i<this.state.addSetsCounter;i++){
sets.push(<AddSets key="AddSets-{i}"/>);
}
Then simply render your {sets}.
I cannot fully test that right now, I wrote that from the top of my head, just play with the above, at least I hope it points you in a right direction.
I am trying to render out a list of object data using FlatList in my React Native component, however I am getting a blank screen without any errors on the console which is why it is rather difficult to get to the bottom of the issue here. The data is made available to the component using Redux-Saga approach and supplied to the FlatList which is showing up a blank screen without any errors. To double check if the FlatList is working fine I did a mockup array in component and passed to the FlatList which renders out the UI as expected. Following is the code I am using here;
=======================================================
class Mobile extends Component {
componentDidMount() {
let { readPostsAction } = this.props;
readPostsAction();
}
renderItem = ({ item }) => {
return (
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('HomeDetails', { item })}>
<Card>
<CardItem header>
<Text style={styles.titleHeading}>{item.title}</Text>
</CardItem>
<CardItem cardBody>
<Content style={styles.cardContainer}>
<CustomCachedImage
component={FitImage}
source={{ uri: contentURL(item.attachments[0].url) }}
style={{ width: width, height: 200 }}
/>
<HTML tagsStyles={bodyText} html={reduceStringLength(contentText(item.excerpt))} />
</Content>
</CardItem>
</Card>
</TouchableOpacity>
</View>
)
}
keyExtractor = (item, index) => item.id;
render() {
const { dataSource } = this.props;
console.log('this.props', this.props);
return (
<View>
<FlatList
data={dataSource}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
</View>
);
}
}
function mapStateToProps({ launchAppReducer }) {
return {
isLoading: launchAppReducer.isLoading,
dataSource: launchAppReducer.data
}
}
export default connect(mapStateToProps, { readPostsAction: actions.readPostsAction })(Mobile);
=======================================================
Here is the screenshot of the console showing that the data is available in the component.
Modify your FlatList code and retry
<FlatList
data={dataSource}
extraData={this.props}
keyExtractor={this.keyExtractor}
/>
There was the problem at in the Actions, I was firing readPostsActions instead I should have fired readMobilePostsActions - it works fine now, thank you guys all the all input and help here.
Regards
You just need to add this style in your Flatlist:
<FlatList
style={{height:'100%'}}
data={dataSource}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>