Top navigation items in header constantly flickers when typing - javascript

The image in the header constantly flickers when I type. May I ask how do I stop this flickering at the top right hand corner or accessoryRight? I am using this TopNavigation component from UI Kitten UI library. I don't think this is normal, it shouldn't happen at all. I must be doing something wrongly.
https://youtu.be/fQppQn-RzeE (How do I embed this? Editor, thank you in advance!)
The flickering happens in the title and the right side of the Navigation Header.
I made a separate component for the TopNavigation and then call it in respective screens.
Things I have tried:
Since the outcome of the Header relies on navigation props, I tried using useState and useEffect (with navProps as the dependency) to save the prop instead of reading directly from props, but to no avail
Directly adding the jsx into the TopNavigation's accessoryLeft/Right and title options
Any input is welcome, appreciate it!
TopNavigation:
const NavHeader = ({ navProps }) => {
const navigateBack = () => {
navProps.navigation.goBack();
};
const [type, setType] = React.useState('');
React.useEffect(() => {
setType(navProps.type);
}, [navProps]);
const BackIcon = props => <Icon {...props} name='arrow-back' />;
const BackAction = () => (
<TopNavigationAction icon={BackIcon} onPress={navigateBack} />
);
const renderBrand = () => (
<View style={styles.titleContainer}>
<Image source={require('../../assets/images/brand.png')} />
</View>
);
const renderLogo = () => (
<Image source={require('../../assets/images/logo.png')} />
);
return (
<TopNavigation
style={styles.topNav}
accessoryLeft={navProps.backNav && BackAction}
accessoryRight={
type === 'register' || type === 'profile' ? renderLogo : null
}
title={type === 'landing' || type === 'auth' ? renderBrand : null}
alignment='center'
/>
);
};
Import example:
<KeyboardAvoidingView
style={styles.kbContainer}
behavior={Platform.OS === 'ios' ? 'padding' : null}
>
<SafeAreaView style={styles.parentContainer}>
<NavHeader navProps={navProps} /> // Imported custom Header component here
<ScrollView>
{other content}
</ScrollView>
</SafeAreaView>
</KeyboardAvoidingView>

Perhaps requiring the images just one time and not on every render may help.
Try adding this at the top of the file (not inside a component function)
const brandImage = require('../../assets/images/brand.png');
const logoImage = require('../../assets/images/logo.png');
And then in your props instead of an inline require use the variables:
const renderBrand = () => (
<View style={styles.titleContainer}>
<Image source={brandImage} />
</View>
);
const renderLogo = () => (
<Image source={logoImage} />
);
Edit:
Since this didn't work, perhaps utilizing useMemo to memoize the components that show the images would work?
Something like
const renderBrand = useMemo(() => (
<View style={styles.titleContainer}>
<Image source={brandImage} />
</View>
),[]);
const renderLogo = useMemo(() => (
<Image source={logoImage} />
),[]);

Related

How to pass onPress to props.children?

I'm trying to make a wrapper component in react-native that I can pass down all its props to the children it wraps around. What I really want is to pass down all function props down to all its children. It looks something like this below. I want the onPress in Wrapper to be called when the TouchableOpacity is pressed.
I tried this below but it doesn't work
const Wrapper = ({children,...props})=>{
return <View {...props}>{children}</View>
}
const App = ()=>{
return (
<View style={{flex:1}}>
<Wrapper onPress={()=>{console.log(2)}}>
<TouchableOpacity/>
</Wrapper>
</View>
)
}
It looks like you're looking to map the children and apply the props to each one. That might look like this:
const Wrapper = ({children,...props})=>{
return (<>
{React.Children.map(children, child => (
React.cloneElement(child, {...props})
))}
</>);
}
(method of mapping the children borrowed from this answer)
const App = () => {
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
// do the action need here here
}}>
<Wrapper >
</Wrapper>
</TouchableOpacity>
</View>
)
}
I would advise you to use hooks function instead
If you try to reuse functions that are related
** useAdd.js **
export default () => {
const addFuction(a, b) => {
// do preprocessing here
return a + b
}
return [addFuction]
}
main componet
import useAdd from "/useAdd";
const App = () => {
const [addFuction] = useAdd()
return (
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={() => {
addFuction(4,5)
}}>
...action component...
</TouchableOpacity>
</View>
)
}
console in useAdd hook.... to see visual changes use the react useState

How to handle onpress for FlatList?

I have a grid of pokemons (below).
I am using flatlist to render the pokemons using the data fetched from an api. When I click on a pokemon I want to display the next page (below)
//Function for FlatList - To render Pokemon images
const renderPokemon = ({ item }) => {
let url = item.url
const idPokemon = url.split('https://pokeapi.co/api/v2/pokemon/')
const link = urlImage + idPokemon[1].substring(0, idPokemon[1].length-1) + ".png"
return (
//Individual images
<View style={styles.pokemons}>
<Image
style={styles.image}
resizeMode='contain'
source={{uri:link}}
/>
<Text style={styles.text}>{item.name}</Text>
</View>
)
}
//App container
<NavigationContainer>
<View style={styles.container}>
<TopBar/>
{/**Pokemon image grid display*/}
<FlatList
numColumns={2}
data={pokemons}
renderItem={renderPokemon}
keyExtractor={pokemon => `key-${pokemon.name}`}
style={styles.container}
onPress={() => alert('clicked')} //WHERE DO I PUT THIS ?
>
</FlatList>
</View>
<NavigationContainer>
But it doesnt seem to be responding. I am using Alert just to test. How to I get the flatlist imaages to handlle onpress so it navigates to the next page and displays the data for that specific pokemon?
Thanks in advance!
You need to add Pressable inside each rendered item, after importing from react-native and add onPress event and do as required in the callback function.(like in your case, navigate to new screen). You can pass pokemonDetailsObj in route params or just the pokemonId, depending on how the Pokemon details Component works.
I would also suggest you to create stack navigator and register your screen components to better use [reactnavigation][1] features.
import { Pressable} from 'react-native';
const renderPokemon = ({ item }) => {
let url = item.url
const idPokemon = url.split('https://pokeapi.co/api/v2/pokemon/')
const link = urlImage + idPokemon[1].substring(0, idPokemon[1].length-1) + ".png"
return (
//Individual images
<Pressable
onPress={() => {
// pass the 'ScreenName' of the Pokemon details component.
props.navigation.navigate('ScreenName', {
// props to be passed to the component like pokemonId or full pokemon Details Obj
})
}}
>
<Image
style={styles.image}
resizeMode='contain'
source={{uri:link}}
/>
<Text style={styles.text}>{item.name}</Text>
</Pressable>
)
}
[1]: https://reactnavigation.org/docs/getting-started

Invalid Hook Call - React Hooks

I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}

How to pass Button component's title into a function in React Native

I want to pass the title of a React Native Button component into a neighbouring function. I am using React Native functional components only for this application.
Here's the component. I would like to pass the title of the button pressed by the user, which will be either 'English' or 'Arabic', into the function submitLanguageSelection so that I can then save that value into useLocalStorage(), a custom hook I wrote to handle AsyncStorage, so that the next time the user uses the app, their language choice will be persisted, and they will not be shown the ChooseYourLanguageScreen again.
All help appreciated, thank you.
const ChooseYourLanguageScreen = ({ navigation }) => {
const [saveData, storedValue, errorMessage] = useLocalStorage();
const [userSelectedLanguage, setUserSelectedLanguage] = React.useState('');
const submitLanguageSelection = () => {
//TODO: receive params from onPress
//TODO: save the data locally
//TODO: navigate to welcome screen
};
return (
<View style={styles.container}>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text style={styles.text}>This is the Choose Your Language Screen</Text>
<View style={styles.buttons}>
<View>
<Button
title={'English'}
onPress={() => submitLanguageSelection()}
/>
</View>
<View>
<Button title={'Arabic'} onPress={() => submitLanguageSelection()} />
</View>
</View>
</View>
);
};
You can simply pass it to the function
<Button title={'Arabic'} onPress={() => submitLanguageSelection('Arabic')} />
And access like below
const submitLanguageSelection = (language) => {
console.log(language);
};
Getting data from a sibling component is an anti-pattern.
The source of the knowledge of the language options is the ChooseYourLanguageScreen component (as seems from your snippet), so it should hold the list of available languages. Having that, you can just iterate through them and render the appropriate components:
<View style={styles.buttons}>
{languages.map((language) => (
<View key={language}>
<Button
title={language}
onPress={() => submitLanguageSelection(language)}
/>
</View>
))}
</View>

is there way to check and unchecked the "check-boxes" from my example?

this is my example that I try to check and unchecked the "check-boxes" but I get confused and i will be happy if someone shows me how it should be done.
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { CheckBox } from 'react-native-elements';
const NewPlaceScreen = props => {
const [checked, setChecked] = useState(false);
return (
<View>
<CheckBox
iconRight
right
title="apple"
checked={checked}
onPress={() => setChecked(true)}
/>
<CheckBox
iconRight
right
title="kiwi"
checked={checked}
onPress={() => setChecked(true)}
/>
</View>
);
};
NewPlaceScreen.navigationOptions = {
headerTitle: 'viewsqq'
};
const styles = StyleSheet.create({
TextStyle: {
fontWeight: 'bold',
color: 'grey'
}
});
export default NewPlaceScreen
thats my example above
You need to set them to the opposite of their previous state when pressed. You can do this by using the setState callback:
onPress={() => setChecked(prev => !prev)}
At the moment your check boxes are both using the same state variable checked so they will stay in sync - changing one will change the other. If this is not what you want, you should create a separate state variable for each checkbox.
UPDATE:
To treat each checkbox independently, you need to create state for each checkbox:
const [isAppleChecked, setIsAppleChecked] = useState(false)
const [isKiwiChecked, setIsKiwiChecked] = useState(false)
return (
<View>
<CheckBox
iconRight
right
title="apple"
checked={isAppleChecked}
onPress={() => setIsAppleChecked(prev => !prev)}
/>
<CheckBox
iconRight
right
title="kiwi"
checked={isKiwiChecked}
onPress={() => setIsKiwiChecked(prev => !prev)}
/>
</View>
)
You need to have a separate state for each box, otherwise they will always show the same thing. And you need to set the new state to the opposite of the old state:
const NewPlaceScreen = props => {
const [appleChecked, setAppleChecked] = useState(false);
const [kiwiChecked, setKiwiChecked] = useState(false);
return (
<View>
<CheckBox
iconRight
right
title='apple'
checked={appleChecked} // use the apple-specific state
onPress={() => setAppleChecked(prevState => !prevState)} // use the new apple state function
/>
<CheckBox
iconRight
right
title='kiwi'
checked={kiwiChecked} // use the new kiwi state
onPress={() => setKiwiChecked(prevState => !prevState)} // use the new kiwi function
/>
</View>
);
};

Categories