Passing props to children in a composition model - reactjs

I'm using composition model a, I have one parent element that have many children.
I'm wondering if it is possible to get the props of the parent element inside the children.
For now i'm passing the same props to all the component (parent and child) but I guess there is better way to do that.
Here the parent element
const ImageTop = ({ results, children }) => {
const navigation = useNavigation();
return (
<>
<ImageBackground
style={{ height: height * 0.25, width: "100%" }}
source={imageToDisplay(results)}
>
<Pressable style={styles.blocArrow} onPress={() => navigation.goBack()}>
<Ionicons name="arrow-back-outline" size={32} color="white" />
</Pressable>
<TransparentsBar results={results} />
</ImageBackground>
<View style={{ marginLeft: 15, paddingTop: 15 }}>{children}</View>
</>
);
};
And here I'm passing components as children of the parent component :
const DetailsEvents = ({ route }) => {
const [results] = useState(route.params.item);
return (
<ScrollView showsVerticalScrollIndicator={false}>
<ImageTop results={results}>
<GenreAndTitle results={results} />
<Address results={results} />
<Date results={results} />
<CTA results={results} />
<Description results={results} />
<OrganizedBy results={results} />
</ImageTop>
</ScrollView>
);
};
So as you can see I'm sending the same props "Results" to all components which is ugly ... I'd like to send the props only to the parent and the children get access to that props. Is there any way to do that ?
Thank you

You can use React Context. This will remove the redundancy regarding sending same props to all those child components separately. What you will be doing is create a context having the value you want your application to use globally and wrap your root elements with that context's Provider. Then the value will be available to all your child components which you can access and modify with useContext.

Related

What does this notation mean in React-Native?

I took over an unfinished react-native project and this notation confused me. An anonymous function is written in curly brackets and given various arguments ( handleChange, handleBlur, handleSubmit, values). Where do we define those arguments ? Where can an anonymous function get the data for these arguments? What exactly is the function of these arguments?
This codeblock was inside the JSX template between various react-native elements.
Entire project was built along TypeScript.
return (
<ScrollView
contentContainerStyle={{
flex: 1,
flexGrow: 1,
padding: spacing.xl,
}}
>
<KeyboardAvoidingView style={{ flex: 1 }}>
<TouchableWithoutFeedback
onPress={Keyboard.dismiss}
style={{ flex: 1 }}
>
<ImageBackground
source={require('#/assets/images/text-bg.png')}
resizeMode="cover"
minHeight="100%"
flex={1}
>
<Box flex={1}>
<Text color="loginHeader" fontSize={36} marginBottom="xl">
Login
</Text>
<Formik
initialValues={{ phoneNumber: '' }}
onSubmit={async values => await submitLogin(values)}
>
{({ handleChange, handleBlur, handleSubmit, values }) => (
<>
<Box width="100%" marginBottom="xl">
<TextInput
keyboardType="phone-pad"
placeholder="Phone Number"
placeholderTextColor={colors.neutral500}
value={values.phoneNumber}
onChangeText={handleChange('phoneNumber')}
onBlur={handleBlur('phoneNumber')}
/>
</Box>
<Button
label="Login"
onPress={handleSubmit}
backgroundColor="buttonBackground"
padding="md"
borderRadius="sm"
shadowColor="black"
shadowOpacity={0.4}
shadowRadius={8.3}
elevation={20}
shadowOffset={{ width: 0, height: 6 }}
/>
</>
)}
{/* <Box width="100%" marginBottom="xl">
<TextInput
secureTextEntry={true}
placeholder="Password"
placeholderTextColor={colors.neutral500}
/>
</Box> */}
</Formik>
</Box>
</ImageBackground>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
</ScrollView>
)
This technique is known as render props. Instead of passing JSX to an element as it's children, you pass a function which takes data from the parent as its arguments. In your example, Formik calls its children prop with those arguments - they are controlled and defined by the Formik component.
This anonymous function is getting passed to another component as children prop, which is a variant of render prop pattern, a pattern used to share functionality between components.
Like the piece of code you posted, a component like Formik manages some functionality for an input, all of which are encapsulated within the Formik component. to share them, it asks you for a prop that should be function, and can be named anything, but mostly render or children.
Inside itself, the Formik component is structured like this:
const handleChange ... // some logic about input being changed
later in it its return it calls that function props you passed like this:
return <>{props.render({handleChange})</>
In your case, Formik is using the children prop, like this:
return <>{props.children({handleChange})</>

Hide picked items in a FlatList

I'm using react-native FlatList to render out a punch on elements the data of which comes from an external API.
I want to prevent the user from picking the same item twice. So the way I'm going about this it hiding the item after they pick it.
If, let's say, I have a state picked like so
const [ picked, setPicked ] = useState(false);
changing it will of course hide all the elements.
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => (
<TouchableOpacity
style={[ styles.holder, { display: picked ? "none" : "flex" } ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
)}
/>
How can I go about changing the state for only one element inside the FlatList??
Is there a better approach you would recommend to do the same job?
try it like this, it will work,
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => picked ? (
<TouchableOpacity
style={[ styles.holder, ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
): undefined}
/>
The easiest way to do this is if you create an object with all the ids and while inserting it you can check it whether it's in that list or not
it'll cost u O(1) time to check.
<FlatList
{/*some other props*/}
data={allCards}
renderItem={(card: ListRenderItemInfo<CardsProps>) => picked ? (
<TouchableOpacity
style={[ styles.holder, ]}
onPress={() => handleChoice(parseInt(card.item.id))}
>
<Card
{/*some card props*/}
/>
</TouchableOpacity>
): undefined}
/>
const map=new Map();
const handleChoice=(id)=>{
if(Map.get(id)===undefined){
Map.set(id,true)
//add the element to ur list where u need it to
}
}

Conditionally rendering a child component into Header in React Native

In my react-native 0.62 app, I created an <AppHeader /> component to render the header so that I don't have to repeat the same code in every screen.
The title and burger menu are standard and always there but in some screens, I want to add one, two or more buttons to the right section -- see below:
So, it would be nice to receive and render a component on the right. If no component is received, then I wouldn't render anything and the right side would be blank.
I'm using native-base so the header understands <Left> and <Right>.
My question is how would I pass a component to my <AppHeader /> and what would the conditional part look like?
Here's my <AppHeader /> component now:
import React from 'react';
import { Header, Left, Body, Right, Button, Icon, Title } from 'native-base';
// Stylesheet
import { styles } from './style-app-header';
const AppHeader = ({ navigation, title, rightSection }) => {
return (
<Header transparent>
<Left style={{ flex: 1 }}>
<Button transparent onPress={() => navigation.navigation.openDrawer()}>
<Icon name="ios-menu" style={styles.icon} />
</Button>
</Left>
<Body style={{ flex: 1 }}>
<Title style={styles.title}>{title}</Title>
</Body>
{
rightSection === null
? <Right />
: <Right style={{ flex: 1 }}>
// How would I render the component here?
</Right>
}
</Header>
);
}
export default AppHeader;
React components can be used just like any other variable value.
const buttons = <button onClick={someAction}>Click me!</button>;
return (
<AppHeader rightSection={buttons}/>
);
// ...
<Right style={{ flex: 1 }}>
{rightSection}
</Right>
// ...

Adjacent JSX elements

I will be very happy if you can help me. I am making react native expo app. I have code that displays information from the database. When I inserted the code to add an image, I had an error, why?
Error:
Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...? (79:12)
<View>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) =>
<Image
source={require('../assets/rose-blue-flower-rose-blooms-67636.jpeg')}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate('Details', {
otherParam: rowData.article_title,
});
}}
>{rowData.article_title}</Text>
}
/>
</View>
<View>
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<>
<Image
source={require("../assets/rose-blue-flower-rose-blooms-67636.jpeg")}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate("Details", {
otherParam: rowData.article_title,
});
}}
>
{rowData.article_title}
</Text>
</>
)}
/>
</View>;
This issue is that you're rendering in your expression two components that are on the same level. so you must have a JSX fragment or another div as a single parent in your return statement of the renderRow.. I believe the above code should solve the problem
You just need to wrap the elements with a Fragment element. The reason why is ListView requires one element to work off of for a "row". You can simulate this with the fragment :)
<React.Fragment>
<Image />
<Text />
</React.Fragment>
theres some nice syntax for fragments if your compiler supports it
<>
</>
To apply it to your code
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<React.Fragment>
<Image
source={require("../assets/rose-blue-flower-rose-blooms-67636.jpeg")}
/>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate("Details", {
otherParam: rowData.article_title,
});
}}
>
{rowData.article_title}
</Text>
</ React.Fragment>
)}
/>

React Native, I can't tap without closing keyboard

Here is my phone screen I tried ScrollView with keyboardShouldPersistTaps, but it didn't work. I have a ScrollView for Autocomplete suggestions, and when user can types there, they should also be able to select from suggestions. However, without closing keyboard, it is not possible in my case. Here is my work
<ScrollView
scrollEnabled={false}
keyboardShouldPersistTaps={true}>
<View style={{ maxHeight: 220 }}>
<ScrollView style={Style.suggestionContainer}
scrollEnabled={true} >
{this.state.showOptions.map(this.renderSuggestions)}
</ScrollView>
</View>
</ScrollView>
.
.
.
private renderSuggestions(option: MultiInputQuestionOption) {
return (
<TouchableOpacity onPress={this.addSelection.bind(this, option)} >
<Text style={Style.suggestions}>
{option[this.props.titleKey]}
</Text>
</TouchableOpacity >
)
}
Is there any possible solution?
You need to pass the key keyboardShouldPersistTaps=‘handled’ on scroll view which contains the TextInput:-
<ScrollView keyboardShouldPersistTaps=‘handled’>
...
<TextInput />
</ScrollView>
And if you are having issue inside of a modal then You need to pass key keyboardShouldPersistTaps=‘handled’ on All scrollview which are in component stack for the screen. In the ancestors/parent of the Modal also.
Like in my case:
const CountryModal=(props)=>{
return(
<Modal
visible={props.visible}
transparent={false}
{...props}
>
<ScrollView keyboardShouldPersistTaps=‘handled’>
…
</ScrollView>
/>
)
}
In Parent class:
In the parent class where ancestors of modal is there. You need to pass key keyboardShouldPersistTaps=‘handled’`.
class Parent extends Component{
render(){
return(
<ScrollView keyboardShouldPersistTaps=‘handled’> // pass ‘handled’ here also
…
<CountryModal /> // Its the same modal being used as a component in parent class.
</ScrollView>
)
}
Try adding
keyboardShouldPersistTaps={'always'}
to the second ScrollView as well.
Use the 'handled' value for keyboardShouldPersistTaps property because the true value is deprecated. use the keyboardShouldPersistTaps in two ScrollViews and handle your keyboard state in somewhere else by using Keyboard.dismiss() function.

Resources