How to change code depending on onPress state? - react-native

I am using react native and expo.
I'd like to change height and add some Views when I click the box.
How to add some Views seems difficult for me.
So I've tried to change height on CSS first.
const [style, setStyle] = useState(true);
I set 'style' as using useState.
Default value of style is 'true'.
<Header onPress={() => setStyle(!style)}>
And I made it so that if I click Header box, setStyle changes into reversed value of style.
And on css code of box that I wanted to apply change, I use props to be changed depending of the style's state.
height: ${(props) => (props.style ? "60px" : "260px")}
When I console.log the state value, it changes true to false and false to true correctly whenever I click the Header box.
But CSS style is not applied accordingly.
How can I make it applied?
And if you can teach me how to add some View more depending on the state, it would be really helpful.

You haven't included enough code to easily understand the structure of your app, or what exactly you're trying to do. Here's an example that should solve some of your issues... but you'll have to figure out how that fits your particular structure:
function App() {
const [active, setActive] = useState(false);
return <View>
<TouchableOpacity onPress={() => setActive(!active)}>
<Text>Press Me</Text>
</TouchableOpacity>
<View style={{height: active ? 260 : 60}}>
{active && <Text>Content</Text>}
</View>
</View>;
}

Related

How to change HeaderBackButton size in react native navigation?

I'm trying to change the back button size in my react native app navigation header because android is giving me a warning that the touch target size is to small (30x30px, wants it to be at least 48x48px). I'm using reactnavigation.
I've worked out you can customize the back button in the header using headerLeft and the HeaderBackButton element like so.
<Stack.Navigator
screenOptions={({route, navigation}) => ({
headerLeft: () => <HeaderBackButton style={{height: 100, width: 100}} onPress={() => navigation.goBack(null)}/>
})}>
However, the height and width styles have no effect. I've also tried fontSize with no effect. Would prefer an approach where I don't have to override headerLeft and reimplement all of the back button default behaviors as well just to change the size.
You can style Stack's original headerLeftStyle with headerBackTitleStyle={{fontSize: 100}}
If you want custom Image, use option 'headerBackImageSource'.
If you still trying custom components, onPress={()=> navigation.canGoBack() ? navigation.goBack() : null} can be used.

Touchable view and activating another components animation

New to react native and in a component, I have a list of views that include a checkbox (react-native-bouncy-checkbox). Each view is wrapped in a TouchableWithoutFeedback(So I can click the entire view, not just the checkbox) and I have a boolean useState to tell the checkbox whether to display the check or not.
The issue I'm at is that I chose the library for the checkbox because the animation when it's clicked looks very nice. However, the animation doesn't play if I hit the view ~ only if I hit the actual checkbox, which is rather small in my app.
Is there any way to tell another component that it needs to act like it was pressed, so it can play its animation?
Code for clarity:
const Task = ({ id, text }: Types) => {
const [checked, setChecked] = React.useState(false);
return (
<TouchableWithoutFeedback onPress={() => setChecked(!checked)}>
<View style={styles.container} >
<BouncyCheckbox
disableBuiltInState={true}
isChecked={checked}
fillColor="blue"
iconStyle={{ borderColor: 'gray' }}
/>
<Text>{text}</Text>
</View>
</TouchableWithoutFeedback>
)
};
Okay figured it out. Apparently React native allows you to create refs to other components and you can use the reference.onPress() to activate the animation.

Why <TextInput> cannot be focused when inside an absolute positioned parent container <View> in React Native?

I have a TextInput component that is located inside a View with a style setting of position: "absolute":
const [searchTerm, setSearchTerm] = useState('Test');
return (
<View style={{position: "absolute"}}>
<TextInput
autoFocus={true}
value={searchTerm}
onChangeText={text => setSearchTerm(text)}
/>
</View>
)
When this style is set, I cannot focus or perform any interaction with the TextInput (including autoFocus={true}) even though I can see the input field and default text (Test). Once I remove the absolute positioning I am able to work with the TextInput as usual.
What is the reason for this behavior? (using Android)
you should add zIndex:400 or some high number it worked for me

visibility hidden on react native not working, to take space even not shown?

I have TouchableOpacity in a space between flex container that I want to take space even not shown,
My code:
<TouchableOpacity
style={showClear && { visibility: 'hidden' }}
onPress={() => this.props.clearCompleted()}>
<Text>Clear Completed</Text>
</TouchableOpacity>
display: none works but it doesn't take space, the code above dont work but does in web?
In my case, I needed to use the element, so I did something like this:
<TextInput style={{opacity: 0, height: 0}} {...props} />
I hope this works for someone else with my problem.
Update
React Native's StyleSheet now supports toggling visibility using display: 'none' and display:flex.
Not all CSS are supported in React Native, that include visibility: hidden or display:none.
To hide a component, not to render it at all, render empty View or null. Or you want to switch a component visibility, verify react's state
<View>
{ !showClear && (
<TouchableOpacity
onPress={() => this.props.clearCompleted()}>
<Text>Clear Completed</Text>
</TouchableOpacity>
}
</View>
showClear is kept in state
As Leu mentioned, you can just render null.
Another option if you want to keep the area used by TouchableOpacity is setting up opacity: 0.0 in style, but then you have to remember to set also disabled={false} in props of the TouchableOpacity, to avoid call clicking action on invisible area
Just setting the opacity to 0 was enough for my use case.
If we want to make sure element takes space on DOM but is kept hidden to user,
Step 1: Hide it from user using opacity: 0 .
Step 2: Disable interactions of hidden element.
Our JSX should be like below:
<IconButton
icon="close"
disabled
style={styles.hiddenElement}
onPress={() => console.log("Pressed")}
/>
Our stylesheet should look like below:
const styles = StyleSheet.create({
...
hiddenElement: {
opacity: 0,
},
...
});

React native flatlist initial scroll to bottom

I am trying to create a chat in React native using a <Flatlist />
Like WhatsApp and other chat apps, the messages start at the bottom.
After fetching the messages from my API, I call
this.myFlatList.scrollToEnd({animated: false});
But it scrolls somewhere in the middle and sometimes with fewer items to the bottom and sometimes it does nothing.
How can I scroll initially to the bottom?
My chat messages have different heights, so I can't calculate the height.
I had similar issue. If you want to have you chat messages start at the bottom, you could set "inverted" to true and display your messages and time tag in an opposite direction.
Check here for "inverted" property for FlatList. https://facebook.github.io/react-native/docs/flatlist#inverted
If you want to have you chat messages start at the top, which is what I am trying to achieve. I could not find a solution in FlatList, because as you said, the heights are different, I could not use getItemLayout which make "scrollToEnd" behave in a strange way.
I follow the approach that #My Mai mentioned, using ScrollView instead and do scrollToEnd({animated: false}) in a setTimeout function. Besides, I added a state to hide the content until scrollToEnd is done, so user would not be seeing any scrolling.
I solved this issue with inverted property and reverse function
https://facebook.github.io/react-native/docs/flatlist#inverted
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
<FlatList
inverted
data={[...data].reverse()}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
You can use this solution in chat component.
I faced the same issue with you and then I moved to use ScrollView.
It is fixed:
componentDidMount() {
setTimeout(() => {
this.scrollView.scrollToEnd();
});
}
<ScrollView ref={(ref) => { this.scrollView = ref; }} style={styles.messages}>
{
messages.map((item, i) => (
<Message
key={i}
direction={item.userType === 'banker' ? 'right' : 'left'}
text={item.message}
name={item.name}
time={item.createdAt}
/>
))
}
</ScrollView>`
Set initialScrollIndex to your data set's length - 1.
I.e.
<Flatlist
data={dataSet}
initialScrollIndex={dataSet.length - 1}
/>
There are two types of 'good' solutions as of 2021.
First one is with timeout, references and useEffect. Here's the full example using Functional Components and Typescript:
// Set the height of every item of the list, to improve perfomance and later use in the getItemLayout
const ITEM_HEIGHT = 100;
// Data that will be displayed in the FlatList
const [data, setData] = React.useState<DataType>();
// The variable that will hold the reference of the FlatList
const flatListRef = React.useRef<FlatList>(null);
// The effect that will always run whenever there's a change to the data
React.useLayoutEffect(() => {
const timeout = setTimeout(() => {
if (flatListRef.current && data && data.length > 0) {
flatListRef.current.scrollToEnd({ animated: true });
}
}, 1000);
return () => {
clearTimeout(timeout);
};
}, [data]);
// Your FlatList component that will receive ref, data and other properties as needed, you also have to use getItemLayout
<FlatList
data={data}
ref={flatListRef}
getItemLayout={(data, index) => {
return { length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index };
}}
{ ...otherProperties }
/>
With the example above you can have a fluid and animated scroll to bottom. Recommended for when you receive a new message and has to scroll to the bottom, for example.
Apart from this, the second and easier way is by implementing the initialScrollIndex property that will instantly loads the list at the bottom, like that chat apps you mentioned. It will work fine when opening the chat screen for the first time.
Like this:
// No need to use useEffect, timeout and references...
// Just use getItemLayout and initialScrollIndex.
// Set the height of every item of the list, to improve perfomance and later use in the getItemLayout
const ITEM_HEIGHT = 100;
<FlatList
data={data}
getItemLayout={(data, index) => {
return { length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index };
}}
{ ...otherProperties }
/>
I found a solution that worked for me 100%
Added the ref flatListRef to my flatlist:
<Flatlist
reference={(ref) => this.flatListRef = ref}
data={data}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
Then whenever you want to automatically scroll to bottom of the list use:
this.flatListRef._listRef._scrollRef.scrollToEnd({ animating: true });
yes you should access the element _listRef then _scrollRef then call the scrollToEnd 🙄
react-native 0.64.1
react 17.0.2
I've struggled on this as well and found the best possible solution for me that renders without a glitch is:
Use inverted={-1} props
Reverse the order of messages objects inside my array with data={MyArrayofMessages.reverse()} in my case data={this.state.messages.reverse()} using reverse() javascript function.
Stupidly easy and renders instantaneously !
Use inverted={1} and reverse your data by using the JS reverse function. It worked for me
<FlatList contentContainerStyle={{ flex: 1, justifyContent: 'flex-end' }} />
I am guessing that RN cannot guess your layout so it cannot know how much it needs to "move". According to the scroll methods in the docs you might need to implement a getItemLayout function, so RN can tell how much it needs to scroll.
https://facebook.github.io/react-native/docs/flatlist.html#scrolltoend
Guys if you want FlatList scroll to bottom at initial render. Just added inverted={-1} to your FlatList. I have struggle with scroll to bottom for couple of hours but it ends up with inverted={-1}. Don't need to think to much about measure the height of FlatList items dynamically using getItemLayout and initialScrollIndex or whats so ever.
I found a solution that worked for me 100%
let scrollRef = React.useRef(null)
and
<FlatList
ref={(it) => (scrollRef.current = it)}
onContentSizeChange={() =>
scrollRef.current?.scrollToEnd({animated: false})
}
data={data}/>
If you want to display the message inverted, set "inverted" to true in the flat list.
<Flatlist
data={messageData}
inverted={true}
horizontal={false}
/>
If you just want to scroll to the last message, you can use initialScrollIndex
<Flatlist
data={messageData}
initialScrollIndex={messageArray.length - 1}
horizontal={false}
/>
I spent couple of hours struggling with showing the first message on top without being able to calculate the item's height as it contains links and messages. But finally i've been able to...
What i've done is that i wrapped the FlatList in a View, set FlatList as inverted, made it to take all available space and then justified content. So now, conversations with few messages starts at top but when there are multiple messages, they will end on bottom. Something like this:
<View style={ConversationStyle.container}>
<FlatList
data={conversations}
initialNumToRender={10}
renderItem={({ item }) => (
<SmsConversationItem
item={item}
onDelete={onDelete}
/>
)}
keyExtractor={(item) => item.id}
getItemCount={getItemCount}
getItem={getItem}
contentContainerStyle={ConversationStyle.virtualizedListContainer}
inverted // This will make items in reversed order but will make all of them start from bottom
/>
</View>
And my style looks like this:
const ConversationStyle = StyleSheet.create({
container: {
flex: 1
},
virtualizedListContainer: {
flexGrow: 1,
justifyContent: 'flex-end'
}
};