How to rerender a component from a stack navigator - javascript

Each book passes its contents to the VocabList screen when clicked using stack navigator
-State of books is being tracked here
<View style={styles.listContainer}>
<View>
<FlatList
data={bookArray}
renderItem={({ item }) => (
<Book
wordCountProp={item.wordCount[0]}
key={item.id}
uuid={item.id}
title={item.text}
list={item.definitionArray}
changeModalVisible={changeModalVisible}
contrivedRender={contrivedRender}
handleDeleteComponent={(uuid) => handleDeleteBook(uuid)}
handleFavoriteBook={(uuid) => handleFavoriteBook(uuid)}
handleUnFavoriteBook={(uuid) => handleUnFavoriteBook(uuid)}
navigateStackHandler={(
id,
definitionArrayParam,
setDefinitionArray
) =>
props.navigation.navigate(
"VocabList",
item,
id,
definitionArrayParam,
setDefinitionArray
)
}
/>
)}
/>
</View>
</View>
This is how data is read from the book and displayed on the screen depending on which book you click
State of definitions isn't being tracked on this screen, rather by each independent book component
<View style={styles.listContainer}>
<FlatList
data={props.navigation.getParam("definitionArray")}
renderItem={({ item }) => (
<Definition
key={item.id}
uuid={item.id}
title={item.text}
handleDeleteComponent={(id) => {
props.navigation
.getParam("definitionArray")
.splice(
props.navigation
.getParam("definitionArray")
.indexOf(
props.navigation
.getParam("definitionArray")
.filter((definition)=>definition.id === id)[0]
),
1
);
if (props.navigation.getParam("wordCount")[0] > 0)
props.navigation.getParam("wordCount")[0] -= 1;
}}
navigateStackHandler={(id, definitionArra) =>
props.navigation.navigate("VocabList", item, id, definitionArra)
}
/>
)}
/>
</View>
The problem arises when I update the contents inside of the book such as adding a new vocab word.
The word count stays the same unless I exit the screen
And finally click on the make-shift button I have to force the book component to rerender
What can I do to force the book to rerender as soon as it is updated with a new vocabulary word, or a word is deleted?
I tried keeping track of definition state inside of the VocabList screen but then again I can't communicate with the book component because the contents of the book were simply passed as parameters to the VocabList screen using stack navigator. The VocabList screen doesn't even know the book exists.

Related

How to add custom component inside an Flatlist in React Native?

I have a Flatlist on react native by which its working perfectly, recently saw an UI which has a custom designed compoenent in between the list, kindly check the below reference image
Check this image, a new design named "Safety+" has been added inside an list, How to do this ?
Need to add custom design like below randomly inside and already rendered flatlist ! How to achieve this , as i couldn't find or understand where to start
Please check the image and help in achieving this:
<FlatList
contentContainerStyle={{ paddingBottom: 50 }}
data={this.state.availableBusList}
keyExtractor={item => item.id}
renderItem={({item}) => {
return(
<TouchableOpacity style={styles.busCardContainer}
onPress={() => {
console.log(item);
//this.props.navigation.navigate('SeatLayout')
}}
<Text>{item.name}</Text>
>)}}
/>
This is my flatlist code
You can return a fragment with your component and a randomly included component. The condition for inclusion is up to you, i.e. complete chance, every 5th element, etc.
<FlatList
contentContainerStyle={{ paddingBottom: 50 }}
data={this.state.availableBusList}
keyExtractor={item => item.id}
renderItem={({item}) => {
return(
<Fragment>
<TouchableOpacity style={styles.busCardContainer}
onPress={() => {
console.log(item);
//this.props.navigation.navigate('SeatLayout')
}}
>
<Text>{item.name}</Text>
</TouchableOpacity>
{Math.random() < 0.5 && ( // example 50% chance to include random component
<RandomComponent>...</RandomComponent>
)}
</Fragment>
)}}
/>
You can render conditionally in your renderItem function: https://reactjs.org/docs/conditional-rendering.html
Additionally, if you want to render your custom component at specific indexes, you can also put index parameter into renderItem. Here is my example:
<FlatList
contentContainerStyle={{ paddingBottom: 50 }}
data={this.state.availableBusList}
keyExtractor={item => item.id}
renderItem={({ item, index }) => {
return index % 5 === 0 ? <CustomComponent /> : <NormalComponent />;
}}
/>

Passing flatlist render "Item" to a function. React Native (kinda a js question)

Right now I have a flatlist that contains a bunch of firebase objects (books), when one of the book are clicked, I want to return a page with more data about that specific book. Right now each book it an object, where title is one of the values, this is what is shown on the flatlist. I want to be able to show all of the other object attributes when the new detailed page is opened. If there is a better way of doing this, let me know but this is the logic that I was trying to go with.
(this is in the flatlist)
renderItem={({ item }) => (
<TouchableOpacity onPress={bookOnPressHandler} activeOpacity={0.9} style={styles.flatListStyle}>
<View>
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
then the handler:
const bookOnPressHandler = (item) => {
//this holds the title of the book
title = item.title
console.log(title)
navigation.navigate('booknotes')
}
I obviously need to pass item into the function, what it is right now wont work. How would I get access to "Item" in the function? once I set the item = to something, I can use it on the new page.
I feel like there is a better method than this and that this might even not work? I know this is a common thing to do in apps, all help is really appreciated. Also sorry if its obvious, Im pretty new to this language and framework!
Since you already have access to the required object that is to be passed to the next screen, it's very easy, you can pass it along with the navigation object as a route param.
It's always good to isolate the prop functions to separate functions to avoid unnecessary re-render of the component.
Here is an example.
<FlatList
ref={ref}
contentContainerStyle={styles.contentContainer}
scrollEventThrottle={16}
numColumns={2}
data={exploreData}
initialNumToRender={2}
renderItem={renderItem}
keyExtractor={(item: any) => item.id.toString()}
})}
/>
Here is the renderItem function:
const renderItem = ({ item }) => {
const handleOnPress = () => navigation.navigate("Profile", { item });
return (
<TouchableWithoutFeedback
onPress={handleOnPress}
>
<ImageBackground
source={{ uri: img }}
style={styles.image}
imageStyle={styles.background}
/>
</TouchableWithoutFeedback>
);
};
//Profile Screen
const Profile = ({ navigation, route }) => {
const { item } = route.params;
console.log(item);
};

React-Native: FlatList re-rendering every single item

So I have a FlatList that gets fed an array of items. When I scroll to the bottom, I append more items to the end of that array and show to the user.
The issue is every single is item is rendered when we add to our item array, and sometimes even rendered twice.
Code is simplified here. /r/reactnative was unable to answer this question.
constructor(props) {
super(props);
this.state = {itemsTest: ['A', 'A', 'A', 'A']}
}
render() {
// Key is fine, since none of my items are changing indexes. I am just adding new items.
return (
<FlatList
keyExtractor={(item,index) => index}
scroll
data={this.state.itemsTest}
renderItem={({item, index}) => <View style={{width: windowWidth}}><Text>{item}</Text></View>
onEndReached={() => this.nextItemsTest()}
onEndReachedThreshold={0.2}
</FlatList>
)
}
nextItemsTest() {
// From suggestions below, just add an element to this array.
console.log('nextItemsTest');
const x = ['A'];
// Have worked with many variations of setting state here. I don't think this is the issue.
this.setState((prevState) => ({itemsTest: [...prevState.itemsTest, ...x],}));}
Here's the output. Every single item is re-rendered (twice even) every time my state is set.
I just want to re-render the items that haven't changed. Thank you.
Instead of Using View directly in your flatlist render you can create another component which is a pure component. so it will only re-renders when its data changes . e.g For your case it re-renders each item only once.
here is the solution
1st create a pure component like this
class SmartView extends PureComponent {
render() {
const {item, index} = this.props;
return (
<View style={{height: 300}}>
{console.log('rendering', index)}
<Text>{item}</Text>
</View>
);
}
}
and then replace View with SmartView in your flatlist like this
<FlatList
keyExtractor={(item, index) => index.toString()}
data={this.state.itemsTest}
renderItem={({item, index}) => <SmartView item=
{item} index={index} />}
onEndReached={() => this.nextItemsTest()}
onEndReachedThreshold={0.2}
/>

React Native's FlatList prop renderItem requires extra "item" (item) => {item.item.name}?

Here I am trying to display an array called "posts" in a FlatList.
render() {
console.log(this.props.posts);
return (
<View style={this.styles.container}>
<FlatList
data={this.props.posts}
renderItem={(item) => <Text> {item.name} </Text>}
/>
</View>
);
}
As seen in this console log, the posts array is correctly populated.
But the above code doesn't display any data in the FlatList.
However, in renderItem, if I add an extra "item" property it works.
renderItem={(item) => <Text> {item.item.name} </Text>}
What is the reason for this behavior.
Input of ReactNative's FlatList is not item, but an object containing 3 parameters: item for actual data, index for index and separators object to customize your item component. What you did is naming that object item, and get actual item from the object.
To avoid confusion, consider using ES6 shorthand:
renderItem={({ item, index }) => <Text> {item.name} </Text>}
This is a common behavior. You can get required behavior by doing object destructuring as:
<FlatList
data={this.props.posts}
renderItem={({item}) => <Text> {item.name} </Text>}
/>
If you are rendering complex component, then you might want to do like this for sake of readability of the code however.
<FlatList
data={this.props.posts}
renderItem={this.renderItem} />
renderItem = ({item}) => {
return (
<Text>{item.name}</Text>
)
}
Might wanna look into your question here though.
ReactNative Flatlist - RenderItem not working

Render button in child component only on certain screen

Im using a flat list on 2 different screens.
On the EventListScreen:
this is the main screen and should display all events.
and on the 2nd page UserProfile.js this page should only display that users events.
in both flat lists I'm using a pure component stored in a seperate class, to where the flat lists are i.e
My Question is, I want to display an "Edit" button on the Event.js child component only if the User is on the
UserProfileScreen.js
I have looked up a lot of example but cant really find any that show how to do it
with a child pure component like I'm doing.
Any Help would be greatly appreciated! Thank you
EventListScreen.js
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen.js
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
{...item}
/>}
/>
Event.js
export default class Event extends Component {
render() {
return (
<Card>
<CardSection>
<Text>{this.props.eventName}</Text>
//I want this button to be displayed only if user is viewing
//from the UserProfile.js
<Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>
</CardSection>
<TouchableOpacity
onPress={() => this.props.openEventDetail()}
>
}
You don't need additional properties.
We can assume that the "Edit" button should be available when openEditEvent prop is defined.
Condition in event (using convertion to bool, false for undefined):
<CardSection>
<Text>{this.props.eventName}</Text>
{!!this.props.openEditEvent &&
<Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>
}
</CardSection>
Use propTypes to define openEditEvent prop as a function, optional (not required).
If I understand your problem correctly an option to solve this problem would be to pass a boolean "showable prop" to show the edit button only when required:
EventListScreen.js (Stays the same, we don't show the edit button here)
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
{...item}
/>}
/>
UserProfileScreen.js (we add the shouldShowEditButton prop to event in order to show the button)
<FlatList
data={this.state.events}
// Get the item data by referencing as a new function to it
renderItem={({item}) =>
<Event
openEventDetail={() => this.openEventDetail(item)}
openEditEvent={() => this.openEditEvent(item)}
shouldShowEditButton
{...item}
/>}
/>
Event.js (We add some propTypes and defaultProps to handle the new prop, it won't show the edit button if not specified)
export default class Event extends Component {
render() {
return (
<Card>
<CardSection>
<Text>{this.props.eventName}</Text>
//I want this button to be displayed only if user is viewing
//from the UserProfile.js
{this.props.shouldShowEditButton && <Button onPress={() =>this.props.openEditEvent()}>
{this.props.displayButton}
</Button>}
</CardSection>
<TouchableOpacity
onPress={() => this.props.openEventDetail()}
>
...
...
);
...
}
}
// We add some default propTypes and definitions
Event.propTypes = {
shouldShowEditButton: PropTypes.bool
};
Event.defaultProps = {
shouldShowEditButton: false
};
In this way you're only showing the edit button for the components that have the prop shouldShowEditButton defined, and because its default value is defined as false, the components that don't have the property will behave in the same way they were before.

Categories