React Native - Disable nested scrolling - javascript

I have a parent FlatList which, in its footer, can render another flatlist or a simple scroll view.
const renderFooter1 = () => {
return <ScrollView> ... </ScrollView>;
}
const renderFooter2 = () => {
return <FlatList ... />;
}
// Note: I am not doing this in real life, just an example for handling both possible footers
return (
<FlatList
refreshControl={renderRefreshControl()}
ListHeaderComponent={renderHeader()}
ListFooterComponent={Math.random() * 10 > 0.5 ? renderFooter1() : renderFooter2()}
showsVerticalScrollIndicator={false}
/>
);
The problem I am experiencing is that, sometimes, when scrolling down, the component which scrolls is the footer, and not the parent list.
How can I avoid that behavior?

You can add the prop scrollEnabled={false} to whichever FlatList you don't want to scroll.

Related

How to get the position of a moving element in React Native

I am currently trying to develop an app with draggable elements using React Native and the library react Native draggable (https://www.npmjs.com/package/react-native-draggable).
My goal is to get the position of my Draggable element. (So I can change their color depending on where it is on the screen).
I tried different method but nothing worked for me. Here is the last thing I tried :
return (
<View>
<Draggable
onDragRelease={event =>
{const layout = event.nativeEvent.layout;
console.log('x:', layout.x);
console.log('y:', layout.y);
}
}
x={positionX}
y={positionY}
renderSize={56}
renderColor='#BDFF00'
renderText={'Drag me'}
// onDrag={getPosition}
/>
</View>
)
The code above returns this error (TypeError: undefined is not an object (evaluating 'layout.x'))
I also tried to do this :
const getPosition = (event) => {
console.log(event.locationX)
setPositionX(event.locationX)
setPositionY(event.locationY)
};
const changerColorFunction = () => {
}
return (
<View>
<Draggable
x={positionX}
y={positionY}
renderSize={56}
renderColor='#BDFF00'
renderText={'Drag me'}
onDrag={getPosition}
/>
</View>
)
Based on the react native Draggable documentation, but it is not working too. I think, I didn't fully understand how it should be working. Any help would be great.

Scroll virtual list to specified row

Struggle to make virtualized list scrolled to a particular row after content refresh. Scenario: there are few rows, user scrolls to somewhere in the list, and then triggers an event (e.g. presses a button in the app) that modifies the content of the list items. Ideally the user must see the same row at the top of the scroll view that was before the event occurred.
Here is the snippet from my code where I try to make it work with no success.
class MyList extends React.Component {
state = {
newScrollToIndex: undefined
}
// function triggered from outsided events
onCellContentChanged() {
const index = this.listRowIndex
// Don't know really which one to call exactlty
// Just called everything
this.cellMeasurerCache.clearAll()
this.listView.recomputeRowHeights()
this.listView.measureAllRows()
/*
* Tried this did not work at all.
* const off = this.listView.getOffsetForRow({ index })
* this.listView.scrollToPosition(off)
*/
// This two seem to work equivalently with 50% chance to work correctly.
// this.listView.scrollToRow(index)
this.setState({ newScrollToIndex: index })
}
renderInfiniteList({ height, width }) {
return (
<InfiniteLoader
isRowLoaded={this.isRowLoaded}
loadMoreRows={this.loadMoreRows}
rowCount={this.rowCount}
>
{({ onRowsRendered, registerChild }) => {
return (
<List
style={{ outline: 'none' }}
noRowsRenderer={() => (
<NoRows
loading={
this.state.loadingMoreRows || this.state.loadingFields
}
/>
)}
height={height}
width={width}
overscanRowCount={2}
rowCount={this.listViewRowCount}
rowHeight={this.cellMeasurerCache.rowHeight}
deferredMeasurementCache={this.cellMeasurerCache}
rowRenderer={this.rowRenderer}
scrollToIndex={this.state.newScrollToIndex}
onRowsRendered={(o) => {
onRowsRendered(o)
this.listRowIndex = o.startIndex
}}
ref={(listView) => {
registerChild(listView)
this.listView = listView
}}
/>
)
}}
</InfiniteLoader>
)
}
}
Any clarification on what is the proper way to make of this scenario to work is appreciated.

React native ScrollView resets positon on setState

I have a ScrollView with buttons on it. I populate ScrollView dynamicaly. After I press on the buttons inside the ScrollView, I'm changing the state of the current selected index and the posiotion of ScrollView is resetting.
<ScrollView
ref={ref => {
this._scrollview = ref;
}}
style={styles.scrollview}
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval={64}
snapToAlignment="start"
decelerationRate="fast"
contentContainerStyle={{ paddingRight: 8, paddingLeft: 8 }}>
{this._channels.map((item, index) =>
<TouchableWithoutFeedback
onLayout={event => {
const layout = event.nativeEvent.layout;
this._channelsPositions[index] = layout.x;
}}
key={item.userId}
onPress={() => this._changeIndex(index)}>
<Image
source={{ uri: videos[index][0].channelThumbnails.high.url }}
style={[styles.channelLogo, index == this.state.currentIndex ? { borderWidth: 2 } : {}]}
/>
</TouchableWithoutFeedback>
)}
</ScrollView>
_changeIndex = (newIndex: number) => {
const { currentIndex } = this.state;
if (newIndex == currentIndex) return;
this.setState({
currentIndex: newIndex
});
const newX = 16 + 48 * newIndex
this._scrollview.scrollTo({
x: 16 + 48 * newIndex,
y: 0,
animated: true
})
}
And after I press the button I want ScrollView to move to x position of this button inside the ScrollView.
Demo: https://streamable.com/gu1edy
Update: After i replace scrollview for Flatlist in header i think i understand why is it happening. But i still don't know how to fix it. So the behavior is same, i am getting scroll reset every time i press on the button/cell/item of scrollview/flatlist.
How looks my flatlist with nested flatlist in header
\ |CHANNEL ICON 0|CHANNEL ICON 1|CHANNEL ICON 2| ... \ header with flatlist
\ FIRST ITEM IN FLATLIST WHICH CONTAINS VIDEO OF CHANNEL\ cell 0
\ SECOND ITEM IN FLATLIST WHICH CONTAINS VIDEO OF CHANNEL\ cell 1
...
So after i klick on channel icon all main flatlist populates with new data so if i understand right the header rerendering and repopulates with new data too. So is there a possibility to update header with flatlist that way my scroll in header stays on it's position without resetting?
Hi I have been fixed it by moving Scrollview from my partial Functional Component to main Function Component screen.
For example you have two functions :
component RenderListProducts
screen/page Product
So the solution is move Scrollview of Flatlist inside RenderListProducts and paste it to indside of screen/page Product.
Try it. If you still have this problem, please put comment below.

How to render something in an if statement React Native

Problem
I am using a flatlist in react native, and want to compare to variables in the flatlist, and render a text component if the two variables are equal, but render nothing if they are not. I've tried many methods of doing this, but they nothing has worked. I would love some help figuring out a way to do this! Thank you.
Couple ways that spring to mind straight away. Ill just assume what you are trying to compare but you can switch these variables out for whatever you please. First thing you could do is have your text be conditional inside of your Text component, EG
<Text>{this.state.variable == this.props.stuff ? "RENDER TEXT" : ""}</Text>
or, if you want to emit the Text component when variables are not equal, have a function inside of your class that will return the Text component conditionally
renderMessage = () => {
if(this.state.variable == "stuff"){
return(
<Text>They were equal</Text>
);
}else{
return(
<View></View> // OR WHATEVER YOU WANT HERE
);
}
}
...
//Then in your render function
....
<View style = {styles.whatever}>
{this.renderMessage()}
</View>
just compare your data in renderItem method accordingly
<FlatList
data={this.props.data}
renderItem={this._renderItem}
/>
_renderItem = ({item}) => (
if(item=='somthing'){
return <Text> your test </Text>
}else{
return <Text>some other text</Text>
}
);
if you want to compare your text in component then
<View>
{
item.data == 'somthing'
?
<Text>if</Text>
:
<Text>else</Text>
}
</View>

FlatList item doesn't update when I setState()

I'm building multiple select modal. When user press the item, the item should be marked as 'Checked'.
Problem I added/removed id from id arrays. When I open and check modal, it doesn't show 'Check' sign. But when I close and open the modal again, it shows 'Check' Sign.
To keep track of selected items, I defined the items in the modal component's state.
state = {
selectedSeasonIds: this.props.selectedSeasonIds,
}
Here is react-native-modal which I use to show modal on the screen
<Modal
isVisible={isSelectorVisible}
onBackdropPress = {() => this.props.hideSelector()}>
<View style={styles.modalContainer}>
<FlatList
style={styles.root}
data={this.props.items}
ItemSeparatorComponent={this._renderSeparator}
keyExtractor={this._keyExtractor}
renderItem={this._renderItemForMultiple}/>
</View>
</Modal>
This is render function for each item
_renderItemForMultiple = ({item}) => {
return (
<TouchableOpacity
style={styles.itemStyle}
onPress={() => {this._handleMultipleItemPress(item.id)}}>
<RkText>{item.value}</RkText>
{ this._renderCheck(item.id) } <<< Here is the problem
</TouchableOpacity>
);
}
When user clicks the item, FlatList's item calls _handleMultipleitemPress
_handleMultipleItemPress = (id) => {
let { selectionType } = this.props;
let { selectedSeasonIds, selectedSizeIds, selectedColorIds } = this.state;
if(selectionType===2) {
if(_.includes(this.state.selectedSeasonIds, id)) {
let newSelectedSeasonIds = _.filter(this.state.selectedSeasonIds, (curObject) => {
return curObject !== id;
});
this.setState({selectedSeasonIds : newSelectedSeasonIds});
} else {
let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
this.setState({selectedSeasonIds : newSelectedSeasonIds});
}
}
// season Select Action
this.props.seasonSelectAction(id);
}
Problem We added/removed id from id arrays. When I open and check modal, it doesn't show 'Check' sign. But when I close and open the modal again, it shows 'Check' Sign.
Somehow the modal is not rendered even eventhough we setState in renderCheck(). Why is it happening? And How can I fix it?
_renderCheck = (id) => {
let { selectionType, selectedSeasonIds, selectedSizeIds, selectedColorIds } = this.props;
if(selectionType===2) {
if(_.includes(this.state.selectedSeasonIds, id)) {
return (<RkText>Check </RkText>);
}
}
return (<RkText> </RkText>);
}
Any other advice will be also appreciated! Thanks for reading this post.
UPDATE I debugged with code and when I press the item, it doesn't go through _renderItemForMultiple. I think it's because I didn't define a param for _renderItemForMultiple. How can I pass item to its param? Any idea?
Even though your state changes, you're not passing it to <FlatList>, so its props don't change. Its shouldComponentUpdate method returns false when none its props change. As the docs state:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
So you need to pass extraData={this.state} to FlatList.

Categories