Flatlist of buttons does not render on first navigation - react-native

I have a Stack.Navigator, when I navigate to a page that contains my FlatList which renders 4x6 items at once, all of the other components will render and the flatlist will be invisible/not rendered. The app then locks up, and then after that the flatlist renders all the items and the layout is bumped around which is not desirable. Is this expected behaviour and what can I do about it? I'd prefer the whole app freeze/stutter until all of the first page is ready to show at once, rather than rendering everything but the Flatlist and then bumping the UI around.
The FlatList component is very uncontroversial as far as I can see.
<View>
<FlatList
numColumns={4}
columnWrapperStyle={styles.colWrap}
keyExtractor={(theitem) => theitem.id}
renderItem={renderIndividualSound(onSoundButtonPress)}
data={soundPage}
key="soundspage"
/>
</View>

Upon further inspection, I was using a useState() hook that init'd with a blank array, and then using a useEffect(()=>{}, []) hook to update the state once at initialisation, meaning a blank list was passed through for the very first render.

Related

React-Native: How to know when the rendering got finished

I have FlatList in render() function with a reference,
<FlatList
ref={(ref) => { this.flatListRef = ref }}
data={this.state.data}
renderItem={({ item, index }) => this.renderOuterCard(item, index)}
/>
I need to use this.flatListRef to call FlatList's methods, which is possible only when FlatList is rendered. Otherwise following error occurs,
Cannot read the property 'ScrollToIndex' of undefined
(I tried to call FlatList's ScrollTOIndex method)
Infact I called ScrollTOIndex in componentDidMount() method , But still this error occurs. Hence, This is clear that componentDidMount() is called before render() finishes completely.
Can anybody let me know when rendering finishes exactly?
This is clear that componentDidMount() is called before render() finishes completely.
According to this schema the first render phase occur before componentDidMount.
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
 Live example:
https://snack.expo.io/#flexbox/flatlist-with-state-example
I am having the same issue. I do have a Component that uses FlatList and I would like to know when, within the first render, the list has rendered the visible items (ready state).
I have discovered that on the main component the componentDidMount was triggered because the FlatList component have mounted as well. And because FlatList Component asyncronously renders each item they only show up after on componentDidUpdate.
I have struggled for a while and even with the componentDidUpdate approach I could only get the moment where FlatList got new data (not rendered the items).
So I found out that I could intercept the renderItem method to create some logic to better estimate what and when is being rendered. The other option was to have a setTimeout (very hacky) to trigger the work based on a average time (very poor solution).
If someone have a proper approach to be able to know when the FlatList finishes rendering the inView items please share.
Thanks.

How to wait for a function to execute by all components of flatlist?

I want to solve this in a much elegant way but I feel the code currently is pretty messed up. So I have a parent component that contains a Flatlist and a button. The Flatlist contains child components that have checkboxes, on press of the button, I want to check which checkboxes in my Flatlist are ticked. The way I tackled this problem was by passing down a prop into the child component. This is how my parent FlatList and state looks -
constructor(props) {
super(props);
this.state = {
getAllVendorDetails: 0, //This state is to trigger an event(by
incrementing) in the child checkboxes to send the ticked vendors to
this parent component.
};
}
<FlatList
data={this.state.vendorsToPick}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => <SelectVendors getAllVendorDetails=
{this.state.getAllVendorDetails} pushData={(item) =>
this.addVendor(item)} vendorData={item}></SelectVendors>}
horizontal={true}
extraData={this.state.getAllVendorDetails}
showsHorizontalScrollIndicator={false}
/>
On press of the button, I increment the state getAllVendorDetails, which triggers a rerender in my child component and in componentDidUpdate I push the component that has its checkbox ticked, to the parent, as a parameter through a prop - Which inturn calls a function in the parent component that adds these items to an array. All this works perfectly fine, I get all the ticked checkboxes on press of the button. (Maybe there's a better way to do this, idk).
I want to perform an operation ONLY after i've received all the items that have a ticked checkbox in my array, I dont know how I could achieve that, currently I have a delay of 500ms after pressing my button, and then if I check my array, I have all the ticked checkbox items. But for sure there has to be a better way to do this.

Scrolling to the bottom of a ScrollView on initial load

I have a large long image with a few inputs at the bottom in my react native view. When the view loads I want it to be "scrolled" to the bottom of the page -- all the examples I can find online are triggered because of some event that the user does.
I want to call scrollToEnd() but I'm not sure where in the lifecycle the ref will actually be set -- anyone have ideas?
Use scrollToEnd this way. It will take to the bottom of ScrollView
<ScrollView
ref={ref => this.scrollView = ref}
onContentSizeChange={(contentWidth, contentHeight)=>{
this.scrollView.scrollToEnd({animated: true});
}}>
I am not clear with your question but
With react-native Image component we have onLoad prop. I think this is what you are looking for.
try something like:
<Image source={{uri...}} onLoad={(e)=>this.handleImageLoaded(e)}/>
then inside handleImageLoaded method you can use scrollToEnd() with some ref as per your code.
also there are other useful props like onLoadStart/End check here
https://facebook.github.io/react-native/docs/image
Or if you are just waiting for the view to render then to scroll
for that I think componentDidAppear() lifecycle method, if using react-native -navigation by wix
or with react-navigation
willFocus, willBlur, didFocus and didBlur events for the component render cycle.. explained here
https://reactnavigation.org/docs/en/navigation-prop.html#addlistener-subscribe-to-updates-to-navigation-lifecycle

React-native: how to change row's content or style while swiping?

I want to have a functionality similar to Google Inbox (demo), when swiping an item causes a change of the background color and size of the icon.
My implementation has a ListView of Swipeable components as rows and I want to change the content or style of a row (in the right pane for example) based on the swipe position.
In order to have the ListView to re-render while swiping I added a pos state which I set using the onPanAnimatedValueRef prop of the Swipeable.
The problem is that the ListView doesn't re-render and nothing changes.
<ListView
...
renderRow={(data) => (
<Swipeable rightContent={
<View><Text>{this.state.pos}</Text></View>
}
...
onPanAnimatedValueRef={(pan) => {
pan.addListener(val => {
this.setState({
pos: val.x
});
}}>
{data}
</Swipeable>
)}
/>
Do you see any problem with this?
I would wrap the Swipeable in your own component, hold state there and your animation logic. It is not the ListView's responsibility to rerender the whole thing since the data hasn't changed, just your row itself.
Alternatively, you could change the data via setState to the ListView's data source (which presumably was based in state and hence force a rerender but this is a less efficient path)

TouchableHighlight and TouchableOpacity get highlighted on render()

I experience a behaviour where TouchableHighlight and TouchableOpacity reacts visually upon render (onPress is not being called).
One thing is that it looks just a little strange, when I enter the page and my button make a small "blink". This is strange but tolerable. The more frustrating part is that if I alter state for the parent component and thus invoke a re-render(), the button will "blink" again, making all buttons blink whenever I alter state.
Pushing the buttons alters page state, and thus pushing a button makes both buttons "blink".
I use react-redux, but this should not affect this behaviour.
The code below is just for illustration.
render()
{
return(
<View>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<ToucableHightlight> //Click here changes state
<Content/>
</ToucableHightlight>
<View>
);
}
Add activeOpacity in TouchableOpacity and it will force to not blink.
<TouchableOpacity style={styles.opecity} activeOpacity={1}>
I solved the problem. Earlier during my render function i defined the "Content"-components, resulting in new (but alike) components being defined during each update. Placing the definitions of "Content" outside of the render function fixed it, so that the components no longer flashes when the page is re-rendered.
This explains why my component was rendered as a new component upon each render in the parent component, but it does not explain why a TouchableHighlight blinks during its initial render.
Buttons blinking during initial render is acceptable to me - buttons blinking upon any state-change is not.
So I am sufficiently happy now.
Not sure if it's because I'm running a later version, but I found this blinking behavior happens only on the first click.
My solution was putting the code that triggers rerendering in a setTimeout
<TouchableOpacity
onPress={function() {
setTimeout(function() {
_this.setState({myState: 'someValue'})
});
}}
>