Passing reference in react native functional components - javascript

I am new to react-native and I am using reanimated-bottom sheet in my project and I want to control this from other functional component called as Comment.
in my main component I am passing the reference of the sheet as
<Comment ref={ref} />
but when I receive it in my comment component it says cant find variable ref
const Comment =({ref}) => {
....
<TouchableOpacity
style={styles.panelButton}
onPress={() => ref.current.snapTo(1)}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
...
}
all I want is to close the sheet from my comment component without changing it to class component.

ref is predefined property so it won't be passed to the component.
You'd better use callback instead of passing ref.
<Component ref={ref} onPressButton={() => { ref.current.snapTo(1) }} />
const Comment =({ onPressButton }) => {
....
<TouchableOpacity
style={styles.panelButton}
onPress={onPressButton}>
<Text>Cancel</Text>
</TouchableOpacity>
....
}

Related

How to have all component logic inside of it and export it to parent components

I have a modal component that is being used to display a form for the user. This modal accepts three parameters: is_visible (if the modal is shown), toggleModal (state function for managing ifthe modal will show or not), passport_id (irrelevant here). This component is used inside another component, the passport component bellow. This passport component is where the state that manages the visibility of modal is created, and then, as said above, the state function and state variable are passed down to the modal (child component).
This works fine, but it seems wrong to have part of the functionality of the modal defined on its parent. I believe that the state that controls wether the modal will show or not should be defined inside the modal component and then, if it's necessary to use these functions ouside the modal, they should be exported. I've tried this by defining a hook inside the modal and exporting to the passport component, but it didn't work because the state variable inside the modal was not updating.
So my question is if there's a proper way to apply the idea of "Tell, don't ask" (https://martinfowler.com/bliki/TellDontAsk.html) here, making it possible to tell the component to do something instead of passing down data. Or, in other words, put the logic of the modal only inside the modal component
Modal component:
const TransferPassportModal = ({is_visible, toggleModal, passport_id}) => {
const [input_text, setInputText] = useState(''),
[loading, setLoading] = useState(false);
function closeModal() {
toggleModal(false);
}
return (
<Modal animationType="fade" transparent={true} visible={is_visible}>
<View style={styles.modal}>
<View style={styles.modal_content}>
<TouchableOpacity
onPress={closeModal}
style={styles.close_modal_button}>
<Icons name="close" size={20} />
</TouchableOpacity>
// Rest of code
</View>
</View>
</Modal>
);
};
export default TransferPassportModal;
Passport component:
const Passport = ({passport}) => {
const [toggleModal, setToggleModal] = useState(false);
function openModal() {
setToggleModal(!toggleModal);
}
return (
<View style={styles.wrapper}>
<View style={styles.infos_wrapper}>
<Text style={styles.name}>{passport.nome}</Text>
<Text style={styles.text}>{passport.codigo}</Text>
</View>
<View style={styles.buttons_wrapper}>
// Resto of code
<TransferPassportModal
is_visible={toggleModal}
passport_id={passport.ingresso}
toggleModal={setToggleModal}
/>
</View>
</View>
);
};
export default Passport;
I lately do this like so:
export const OpenModalAction = () =>{
return (
<>
<Button/>
<Modal> …. <Modal/>
</>
)
}
So the button and modal is one component

How to do a this with a component in react native?

I am working with react native and expo.cli and would like to know how I can manage to add a handle to a TouchableOpacity, I have the following component:
function MainModal (){
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={this.TouchableOpacity}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
What I want to do is make a reference to this specific TouchableOpacity when it is selected, but when I use a "this" nested in the component I get the following error: Undefined is not an object (Evaluating 'this.TouchableOpacity') I know maybe This question is a bit of a novice and has more to do with how 'this' works in javascript than with React Native itself, however I can't find a way to make a reference to the selected object so that it executes something when selected. How could I do it?
You can use React.useRef() hook to create a reference to that element so you can access then to it. The idea you have of this is not the actual this. This might refer to the global object (in not strict) and might be undefined in strict mode. If you want to use this in a component I recommend you to create a class component instead of a function one. See this in MDN to learn more of it.
Anyway, the use of ref in a functional component would be this:
function MainModal (){
const touchableReference = React.useRef()
const handleTouchableClick = () => {
console.log(touchableReference.current)
//
// Outputs HTMLDivElement...
//
}
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
ref={touchableReference}
onPress={() => handleTouchableClick()}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
)
}
One standard way of passing event handler in functional component is as follows
function MainModal (props){
const onPress1 = useCallback(()=>console.log); // created inside the componet
const {onPress2} = props; // passed from props
const {onPress3} = useSomeHook(); // from context/hook
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={onPress1}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress2}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress3}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
);
}
The behaviour of this works the same in react and react-native. React functional component is executed during the render phase and you are not suppose to work with this here, given that you do not understand when and where it is called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

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>

Using a ref in a FlatList in React Native

I am still having trouble understanding ref's in React Native (and React in general). I am using functional component. I have a FlatList that has many items. How do I create a reference for a thing within an item like a Text or View component?
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => 'text' + item.id = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
Then in _editItem I want to reference the Text component so that I can change its text from 'EDIT' to 'EDITING', or even change its style, or whatever.
_editPost = id => {
console.log(text + id)
}
I have tried...
FeedComponent = () => {
let editPosts = {}
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => editPosts[item.id] = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
...and a few other things, but I think I might be way off so I could use some guidance.
Typically you don't use refs in react to update content like text. Content should be rendered based on the current props and state of your component.
In the case you describe you'll probably want to set some state in the parent component that then impacts the rendering of the item.
As a sidenote refs are used if you need to trigger a method on a child component like calling focus on a TextInput for example but not for imperatively updating component content.
In your case you'll want to update some state representing the current active item. Something like:
import React, {useState} from 'react';
FeedComponent = () => {
const [activeItem, setActiveItem] = useState(null);
<FlatList
data={data}
renderItem={({ item }} => {
return (
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => setActiveItem(item.id)}>
{activeItem === item.id
? <Text>EDITING</Text>
: <Text>EDIT</Text>
}
</TouchableOpacity>
</View>
);
}
extraData={activeItem}
/>

React Native: filtering props?

I created a basic component such as:
export default (props) => (
<TouchableOpacity {...props} style={styles.button}>
{props.title && <Text style={styles.text}>{props.title}</Text>}
{props.icon && <Icon name={props.icon} />}
</TouchableOpacity>
);
I can then call it with <Component title="Home" icon="home" /> for instance.
The problem is that passing {...props} to TouchableOpacity generate errors because it does not recognize title nor icon properly.
For instance:
JSON value 'Home' of type NSString cannot be converted to...
Is there a way to filter props so that I only pass valid ones for TouchableOpacity?
Transferring Props
Sometimes it's fragile and tedious to pass every property along. In that case you can use destructuring assignment with rest properties to extract a set of unknown properties.
List out all the properties that you would like to consume, followed by ...other.
var { checked, ...other } = props;
This ensures that you pass down all the props EXCEPT the ones you're
consuming yourself.
function FancyCheckbox(props) {
var { checked, ...other } = props;
var fancyClass = checked ? 'FancyChecked' : 'FancyUnchecked';
// `other` contains { onClick: console.log } but not the checked property
return (
<div {...other} className={fancyClass} />
);
}
ReactDOM.render(
<FancyCheckbox checked={true} onClick={console.log.bind(console)}>
Hello world!
</FancyCheckbox>,
document.getElementById('example')
);
Like Paul Mcloughlin, I would recommend using object destructuring along with a rest parameter. You can destructure your props object directly in your function parameters like so:
({title, icon, ...remainingProps}) => (...)
This extracts the title and icon props from your props object and passes the rest as remainingProps.
Your complete component would be:
export default ({title, icon, ...remainingProps}) => (
<TouchableOpacity {...remainingProps} style={styles.button}>
{title && <Text style={styles.text}>{title}</Text>}
{icon && <Icon name={icon} />}
</TouchableOpacity>
);

Categories