React native animation with interpolating not working - react-native

I have onboarding and dot component . I want to use opacity animation with interpolate on dots. But it is not working. Do I use onScroll in a wrong way or what ? Thanks to any help
const Onboarding = () => {
const { width } = useOrientation();
const scrollX = new Animated.Value(0);
const onScroll = (event: any) => {
Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }]);
};
return (
<Box flex={1} backgroundColor="mainBackground">
<Box flex={3}>
<Animated.ScrollView onScroll={onScroll} horizontal>
{slides.map((data, index) => (
<Slide key={index} data={data} {...{ index, scrollX }}
/>
))}
</Animated.ScrollView>
</Box>
<Box
flex={1}
>
{slides.map((_, index) => (
<Dot key={index + 1} currentIndex={divide(scrollX, width)} {...{ index }}
/>
))}
</Box>
</Box>
);
};
const Dot = ({currentIndex,index}:DotProps) => {
const opacity = interpolate(currentIndex,{
inputRange:[index-1,index,index+1],
outputRange:[0.5,1,0.5],
extrapolate:Extrapolate.CLAMP,
})
return (
<Animated.View
style={{ opacity, backgroundColor: "#2CB9B0",width: 8,height: 8,borderRadius: 4
}}
/>
);
};

Related

Remove an item from FlatList render wrong items

I know that it has been asked several times, but in all the other threads the problem is that the author is manipulating the state directly - which I don't (hopefully).
I've got an array of posts that I get from the DB.
I want to be able to filter this array according to the tags each post has. In order to do so, I'm filtering the array, saving the result in a temp array, and then setting another array that holds the current posts to display again using useState. The filter works properly.
The list of posts is rendered in a FlatList
<FlatList
data={filteredPosts}
extraData={refreshFlat}
style={globalStyles.feed}
renderItem={({ item }) => (
<Post
post={item}
navigation={navigation}
style={globalStyles.list_of_posts}
/>
)}
refreshing={refreshing}
onRefresh={handleRefresh}
ListEmptyComponent={() => {
return (
<View>
<Text style={globalStyles.be_first}>
נראה שאין מה להציג כרגע..
</Text>
</View>
);
}}
ItemSeparatorComponent={() => {
return <View style={{ height: 12 }}></View>;
}}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={getHeader}
/>
But when I re-render the list, the wrong items are shown.
For example, if the list contains only one post after the filter, the FlatList will display only the first item of the original list.
Just to make clear, the item is the right item, I used console.log both outside and inside the Post component to validate it.
postsList - Holds the original list
filteredPosts - Holds the current posts that I want to display
refreshFlat - I tried to force it to refresh using extraData
The complete component:
import { Text, Pressable, FlatList, View, Modal } from "react-native";
import { globalStyles } from "../../styles/global";
import React, { useState, useEffect } from "react";
import Post from "../../API/Post";
import { useData } from "../../AuthProvider/UserDataProvider";
const FeedScreen = ({ navigation, route }) => {
const [refreshing, setRefreshing] = useState(true);
const { getPosts, tagsList, getTags } = useData();
const [postsList, setPostsList] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [selectedTags, setSelectedTags] = useState([]);
const [filteredPosts, setFilteredPosts] = useState([]);
const [refreshFlat, setRefreshFlat] = useState(false);
const handleRefresh = () => {
getPosts()
.then((posts) => {
setPostsList(posts);
setFilteredPosts(posts);
setSelectedTags([]);
setRefreshing(false);
})
.catch(console.error);
};
const handleSelectTag = (tag) => {
if (selectedTags.includes(tag)) {
const temp = selectedTags.filter((currTag) => currTag !== tag);
setSelectedTags(temp);
} else {
setSelectedTags((prev) => [...prev, tag]);
}
};
const filterPosts = (tags) => {
if (tags.length === 0) return setFilteredPosts([...postsList]);
const temp = postsList.filter((post) =>
post.data.tags.some((t) => tags.includes(t))
);
console.log(temp);
setFilteredPosts(temp);
setRefreshFlat((prev) => !prev);
};
const getHeader = () => {
return (
<View>
<Modal
visible={modalVisible}
animationType="slide"
onRequestClose={() => {
setModalVisible((prev) => !prev);
}}
>
<View>
<FlatList
data={tagsList}
renderItem={({ item }) => (
<Pressable
style={{
backgroundColor: selectedTags.includes(item.name)
? "green"
: "#EAE7E6",
padding: 5,
margin: 5,
}}
onPress={() => handleSelectTag(item.name)}
>
<Text>{item.name}</Text>
</Pressable>
)}
numColumns={3}
ListEmptyComponent={() => {
return (
<View>
<Text style={globalStyles.be_first}>
נראה שאין מה להציג כרגע..
</Text>
</View>
);
}}
ItemSeparatorComponent={() => {
return <View style={{ height: 12 }}></View>;
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
<Pressable
onPress={() => {
filterPosts(selectedTags);
setModalVisible(false);
}}
style={{ marginLeft: 10, width: 50, height: 50 }}
>
<Text>{"סנן"}</Text>
</Pressable>
</Modal>
<Pressable
onPress={() => setModalVisible(true)}
style={{ width: "100%", height: 50 }}
>
<Text>{"open modal"}</Text>
</Pressable>
</View>
);
};
useEffect(() => {
getPosts()
.then((posts) => {
setPostsList(posts);
setFilteredPosts(posts);
setRefreshing(false);
})
.catch(console.error);
getTags();
return;
}, []);
return (
<View style={{ flex: 1 }}>
<FlatList
data={filteredPosts}
extraData={refreshFlat}
style={globalStyles.feed}
renderItem={({ item }) => (
<Post
post={item}
navigation={navigation}
style={globalStyles.list_of_posts}
/>
)}
refreshing={refreshing}
onRefresh={handleRefresh}
ListEmptyComponent={() => {
return (
<View>
<Text style={globalStyles.be_first}>
נראה שאין מה להציג כרגע..
</Text>
</View>
);
}}
ItemSeparatorComponent={() => {
return <View style={{ height: 12 }}></View>;
}}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={getHeader}
/>
<Pressable
title="edit"
onPress={() => {
navigation.navigate("CreateProject");
}}
style={globalStyles.plus_btn}
>
<Text style={globalStyles.plus_btn_text}>+</Text>
</Pressable>
</View>
);
};
export default FeedScreen;
Maybe it's the key extractor issue? You are using the index of the array as the key and it could confuse the flatlist when you update the data.

Is there an optimal way to disable parent scroll

I am trying to disable parent scroll, when scrolling up/down in the child component (child component is a horizontal FlatList). Is there an optimal way?
<ScrollView scrollEnabled={scrollEnabled}>
<Header />
<FlatList
horizontal={true}
data={cards}
renderItem={() => (
<Pressable onPressIn={() => setScrollEnabled(false)} onPressOut={() => setScrollEnabled(true)}>
<ScrollingCard />
</Pressable>
)}
keyExtractor={(item) => item.toString()}
/>
{cards.map((item) => <ScrollingCard key={`vertical-${item}`} item={item} type="Vertical" />)}
</ScrollView>
Declare a state:-
const [data, setData] = useState({
username: "Mark White",
enableScrollViewScroll: true
})
Handle state when scroll:-
const onEnableScroll = (value) => {
setData({
...data,
enableScrollViewScroll: value
})
}
Inside component:-
<ScrollView
showsVerticalScrollIndicator={false}
scrollEnabled={data.enableScrollViewScroll}>
<View>
<FlatList
onTouchStart={() => {
onEnableScroll(false);
}}
onMomentumScrollEnd={() => {
onEnableScroll(true);
}}
...
/>
</View>
</ScrollView>
export const ScrollEnabledContext = createContext(null);
const Parent = () => {
const ref = useRef(null);
const setIsEnabled = (bool) => {
ref.current?.setNativeProps({ scrollEnabled: bool });
};
return (
<ScrollEnabledContext.Provider value={setIsEnabled}>
<ScrollView ref={ref} nestedScrollEnabled={true}>
<ChildScrollView />
</ScrollView>
</ScrollEnabledContext.Provider>
);
};
const ChildScrollView = () => {
const value = useContext(ScrollEnabledContext);
return (
<ScrollView onTouchStart={() => value(false)} onTouchEnd={() => value(true)}>
....
</ScrollView>
);
};
It works without any re-renders and works way faster than setting state.

Decomposing react native component

I've been trying to develop a react native app and I'm stuck here . I want to decompose onboarding component. Here is what I've done . Is it oke or what else can I do more readeble code ? I've divided Slider and SubSlider component but I think it isn't enough .Can I use component instead of functions Any suggestion will help me . What are the best practices in this case ? Thanks
const renderScrollView = (scroll, width: number, x: Animated.Value) => {
return (
<Animated.ScrollView showsHorizontalScrollIndicator={false} ref={scroll} pagingEnabled
onScroll={Animated.event([{ nativeEvent: { contentOffset: { x } } }], {
useNativeDriver: true,
})}
horizontal
>
{slides.map((data, index) => {
return (
<Slider key={index}>
<SliderImage src={data.src} />
<Box paddingHorizontal="l">
<SliderTitle>{data.label}</SliderTitle>
<SliderText>{data.description}</SliderText>
</Box>
</Slider>
);
})}
</Animated.ScrollView>
);
};
const renderSubSlider = (width: number,x: Animated.Value) => {
return (
<SubSlider>
<SubSliderItem>
{slides.map((_, index) => {
return (
<Dot
key={index + 1}
currentIndex={Animated.divide(x, width)}
{...{ index }}
/>
);
})}
</SubSliderItem>
</SubSlider>
);
};
const Onboarding = React.memo(({ onDone }: OnboardingProps) => {
const { width } = useDimensions().window;
const [completed, setCompleted] = useState<boolean>(false);
const scroll = useRef(null);
const x = React.useRef<Animated.Value>(new Animated.Value(0)).current;
React.useEffect(() => {
x.addListener(({ value }) => {
if (Math.ceil(value / width) === slides.length - 1) {
setCompleted(true);
} else {
setCompleted(false);
}
});
return () => x.removeAllListeners();
}, []);
return (
<Box flex={1} backgroundColor="mainBackground">
<Box flex={3}>{renderScrollView(scroll, width, x)}</Box>
{renderSubSlider(width, x)}
</Box>
);
});

react-native FlatList scroll to bottom for chat app

I've made a chat app, and for rendering messages flatlist is used. But the problem is tried to scroll to the end of the screen every time the page is loaded, but it fails to do so. I've tried inverted props, but nothing happened, only the list got inverted.
Even played with ref to make it auto-scroll to the bottom, but nothing happened.
<FlatList
ref="flatList"
onContentSizeChange={() =>
this.refs.flatList.scrollToEnd()}
contentContainerStyle={{
marginBottom:
verticalScale(200)
}}
style={styles.list}
data={this.state.messages}
/>
How to make it scroll to the bottom the screen or scroll to the last index of the message when rendered?
(UPDATE)
IT WAS AN ISSUE WITH THE <Content/> component i used which belongs to native-base . Upon removing and replacing it with a <View/> it works perfectly fine.
Also, for chat based app the inverted prop in Flatlist is the way to implement in right way.
I've added the way i managed to scroll in the answer below. If you simply want your app to display the last item in the list and stays there, you can use inverted
You should use ref like this:
export default class MyAwesomeComponent extends React.Component {
FlatListRef = null; // add a member to hold the flatlist ref
render() {
return (
<FlatList
ref={ref => (this.FlatListRef = ref)} // assign the flatlist's ref to your component's FlatListRef...
onContentSizeChange={() => this.FlatListRef.scrollToEnd()} // scroll it
contentContainerStyle={{marginBottom: verticalScale(200)}}
style={styles.list}
data={this.state.messages}
/>
);
}
}
prueba esto
return (
<View style={{flex: 1}}>
<KeyboardAvoidingView
behavior="padding"
style={styles.keyboard}
keyboardVerticalOffset={height - 1000}>
<FlatList
ref={ref => (this.FlatListRef = ref)}
onContentSizeChange={() => this.FlatListRef.scrollToEnd()} // scroll it
// contentContainerStyle={{marginBottom: verticalScale(200)}}
// keyboardShouldPersistTaps='always'
style={styles.list}
extraData={this.state}
data={this.state.messages}
keyExtractor={item => {
return item.id;
}}
renderItem={e => this._renderItem(e)}
/>
<View style={styles.input}>
<TextInput
// style={{flex: 1}}
value={msg}
placeholderTextColor="#000"
onChangeText={msg => this.setState({msg: msg})}
blurOnSubmit={false}
onSubmitEditing={() => this.send()}
placeholder="Escribe el mensaje"
returnKeyType="send"
/>
</View>
</KeyboardAvoidingView>
</View>
);
You can use Javascript method to reverse to show your messages from end
messages.reverse()
scrollToListPosition = (index) => {
const itemOffset = this.getItemOffset(index)
this.flatListRef.scrollToOffset({ animated: false, offset: itemOffset })
}
getItemOffset = (index) => {
let heightsum = 0
for (i = 0; i < index; i++) {
heightsum = heightsum + this.itemHeight[i]
}
return heightsum
}
render(){
return (
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
data={postList}
keyExtractor={(item, index) => item._id}
horizontal={false}
extraData={this.state}
keyboardShouldPersistTaps='always'
refreshing={this.props.isRefreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
getItemLayout={(data, index) => (
{ length: this.getLength(index), offset: this.getLength(index) * index, index }
)}
renderItem={({ item, index }) => {
return (
<View onLayout={(event) => {
var { height } = event.nativeEvent.layout;
this.itemHeight[index] = height
}}
>
<ListCommon
key={index}
item={item}
index={index}
parentFlatList={this}
data={item}
instance={this.props.commanAction}
tag={this.state.tag}
changeRoute={this.props.changeRoute}
/>
</View>
);
}}
/>
)
}
getLength = (index) => {
if (this.itemHeight[index] === undefined) {
return 0;
}
return this.itemHeight[index]
}
Here is how i solved it:
export default class Test extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
setTimeout(() => {
this.FlatListRef.scrollToEnd();
}, 1500);
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={[1, 2, 3, 4, 5, 6, 7, 8]}
ref={(ref) => (this.FlatListRef = ref)}
renderItem={({ item }) => {
return (
<View
style={{
height: 140,
width: 400,
backgroundColor: "yellow",
alignItems: "center",
justifyContent: "center",
}}
>
<Text>{item}</Text>
</View>
);
}}
/>
</View>
);
}
}

Passing values after clicking on the Button - React Native

How can I pass all values inside Render() method after I click on the button Send? Inside this Render() method I return some Flatlists, Views, Signature, etc. So, is it possible to pass all of these values to another page only by a click of a button.
Please let me know if you dont have the question clear so I can add some more explanation.
See below for the code (Edited).
I appreciate any suggestion or help!
EDIT:
renderTextandInputs = (obje) => {
var keyvalue_to_json = JSON.parse(obje.keyValues);
var foundTextFields = [];
for (let i = 0; i < keyvalue_to_json.length; i++) {
if (keyvalue_to_json[i].type === 'textfield') {
foundTextFields.push(<TextInput style={{ borderWidth: 1, flex: 1, alignItems: 'flex-start' }}>{keyvalue_to_json[i].placeholderText}</TextInput>)
}
}
return (
<View>
<ScrollView>
<ListItem
title={obje.name}
subtitle={obje.description}
/>
</ScrollView>
<View >
{foundTextFields}
</View>
</View>
)
}
render() {
const obj = this.props.navigation.state.params.item;
var propsArray = [];
const itemArray = Object.assign(obj)
propsArray.push(itemArray)
keyExtractor = (item, index) => {
return index.toString();
}
return (
<View>
<View>
<FlatList
key={this.keyExtractor}
data={propsArray}
renderItem={({ item }) => this.renderTextandInputs(item)}
/>
</View>
<View >
{this.state.signature ? (
<Image
resizeMode={"contain"}
source={{ uri: this.state.signature }}
/>
) : null}
</View>
<Modal isVisible={this.state.isModalVisible}
onBackdropPress={() => this.setState({ isModalVisible: false })}
>
<Signature
width="100"
onOK={this.handleSignature}
descriptionText="Sign"
clearText="Clear"
confirmText="Save"
webStyle={style}
/>
</Modal>
<View>
<Button title="SIGN" onPress={this._toggleModal} />
</View>
<View>
<Button title="Send" onPress={this._onSendDoc} />
</View>
</View>
);
}
_onSendDoc = (item) => {
this.props.navigation.navigate('Detail', { item: item })
}
}
if you check here: https://facebook.github.io/react-native/docs/flatlist you can render a button per flatlist item like this:
EDIT
_onSendAll = () => {
const obj = this.props.navigation.state.params.item;
var propsArray = [];
const itemArray = Object.assign(obj)
propsArray.push(itemArray)
this.props.navigation.navigate("Detail", { allData: propsArray });
};
_onSendDoc = item => {
this.props.navigation.navigate("Detail", { item: item });
};
render() {
return (
<FlatList
data={[{title: 'Title Text', key: 'item1'}]}
renderItem={({item}) => (
<TouchableHighlight
onPress={() => this._onSendDoc(item)}
<View style={{backgroundColor: 'white'}}>
<Text>{item.title}</Text>
</View>
</TouchableHighlight>
)}
/>
)
On each button clicked, the item data passed will be logged.