How to initiate props when rendering Component? - react-native

I have a Login Component where I want the user to choose Service from a Service Catalogue. The Picker gets and sets values to redux:
<Picker
selectedValue={this.props.service.id}
onValueChange={itemValue => this.props.setServiceType(itemValue)}>
{service_catalogue.map(service =>
<Picker.Item key={service.id} label={service.id} value={service.id} />
)}
</Picker>
But I don't know how to properly set the initial value. I set the default value in componentDidMount (the first item in the Catalogue), but I think componentDidMount is trigged on update? Is there a lifecycle function that is only triggered on rendering Component in React Native?
componentDidMount() {
this.props.setServiceType(service_catalogue[0].id)
}
So the problem that I'm facing is that even though the user might choose "Instructor" the service becomes service_catalogue[0] = "Cleaner". And if I don't setServiceType on componentDidMount no Picker appears, as this.props.service.id doesn't exist.

You can set default value for the props on the following way:
...
YourComponent.propTypes = {
myState: PropTypes.string,
}
YourComponent.defaultProps = {
myState: 'default value',
};
const mapStateToProps = state = ({
myState: state.myState,
});
export default connect(mapStateToProps)(YourComponent);
More info: https://reactjs.org/docs/typechecking-with-proptypes.html#default-prop-values

Related

React-Native Switch uncontrolled using an object doesn't change value

I have a react-native project in which I'm trying to handle the Switch component in a way that I can dynamically set an object with the boolean and its value, but I doesn't respond well:
Initially I use useEffect to process the data:
useEffect(() => {
let lista = {}
Object.entries(planes).forEach(([j, plan]) => {
lista[plan.nombre] = false
})
setSeleccion(lista)
}, []);
Then I loop through my data to load it and create the "card" dynamically with a Switch:
<Switch
offTrackColor="indigo.100"
onTrackColor="indigo.300"
onThumbColor="coolgray.500"
offThumbColor="indigo.50"
size="lg"
onToggle={(val) => toggleSwitch(val, plan.nombre)}
value={seleccion[plan.nombre]}
/>
Then toggle function:
const toggleSwitch = (val, plan) => {
setSeleccion({...seleccion, [plan]: val})
};
Now if I print the new array it shows as expected:
useEffect(() => {
console.log(seleccion)
}, [seleccion]);
But sadly the Switch remains as false visually (just goes back to its false state, so one cant toggle back to off/false).
If I do a toString(seleccion[plan.nombre]) in the render it shows [object Undefined]
Assuming that the Switch that you used is same/from react-native. There shouldn't be a prop called onToggle in Switch like you wrote below.
<Switch
offTrackColor="indigo.100"
onTrackColor="indigo.300"
onThumbColor="coolgray.500"
offThumbColor="indigo.50"
size="lg"
onToggle={(val) => toggleSwitch(val, plan.nombre)}
value={seleccion[plan.nombre]}
/>
instead, you need to use onValueChange
<Switch
offTrackColor="indigo.100"
onTrackColor="indigo.300"
onThumbColor="coolgray.500"
offThumbColor="indigo.50"
size="lg"
onValueChange={(val) => toggleSwitch(val, plan.nombre)}
value={seleccion[plan.nombre]}
/>
You can read more about the props in Switch here

How do I set a listener prop on React Native `TextInput` ref

I have a need to dynamically set the OnPressIn prop of a TextInput. I have a ref to the TextInput, but can't figure out how to set this method.
const inputRef = useRef(null);
<TextInput
...
ref={inputRef)
/>
// inputRef is passed as a prop to another component, then I try to set OnPressIn. Neither of these techniques work:
inputRef.setNativeProps({
OnPressIn: () => console.log('ONPRESS TRIGGERED'),
});
inputRef.OnPressIn = () => console.log('ONPRESS TRIGGERED');
How can I set this prop on a TextInput ref?
I believe you're missing "current".
inputRef.current.setNativeProps({
OnPressIn: () => console.log('ONPRESS TRIGGERED'),
});
Also, on what occasion does your onPressIn need to be changed? Isn't using a condition easier?
const handleOnPressIn = () => {
if (condition) {
console.log(`ONPRESS TRIGGERED condition: ${condition}`)
} else {
console.log(`ONPRESS TRIGGERED condition: ${condition}`)
}
}
<TextInput
onPressIn={() => handleOnPressIn()}
/>
If you're aiming for performance optimisation, I believe you should also consider wrapping the setNativeProps call in a useCallback() hook, as from the React Native docs on setNativeProps to edit TextInput value.

Facing issue with React-hook forms

I copied/paste the first example of the React Hook Forms from NB 3.0 and getting this error. TypeError: errors is not an Object. (evaluating '"firstName" in errors'). Any idea why?
The example provided here Native Base V3 React-Hook_forms does not use the controller correctly to get onChangeText state and to catch errors for the value, to fix this change the following;
Change the line: const { control, handleSubmit, errors } = useForm(); to const { control, handleSubmit, watch, formState: { errors } } = useForm();
In the Controller change render={({ onChange, onBlur, value }) to render={({ field: { onChange, onBlur, value } })
In the Input component change onChangeText={(val) => onChange(val)} to onChangeText={onChange}
Reference is from the integration example on the React-Hook-Form website here: React Hook Form with React Native

access `headerRight` inside a screen that is hosted by stack navigator

I have created a stack navigator:
import {createStackNavigator} from '#react-navigation/stack';
const TheStack = createStackNavigator();
Then, This is my navigator, it claimed component={LandingScreen}:
<TheStack.Navigator ...>
<TheStack.Screen
name="LandingScreen"
component={LandingScreen}
options={{
title: '',
headerLeft: null,
headerRight: () => (
<MyHeaderRightComponent />
),
}}
/>
<TheStack.Navigator>
As you can see above in options of the screen, there is headerRight, I have declared using MyHeaderRightComponent as headerRight so that it is shown on the right side of the header on screen.
Here is my LandingScreen.js :
import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
const LandingScreen = ({navigation}) => {
// How can I access the `headerRight` component I have set above from here?
...
}
My question is how can I access the headerRight inside my LandingScreen.js? I know I can update or reset the headerRight by:
navigation.setOptions({headerRight:() => <NewHeaderRightComponent/>})
But now what I need is to access the previous already set component, not setting a new one. How to do that?
Edits to the answer as per the request received in comments. The answer is the same. This is just further demonstration on how to use it.
// The screen component where you want to pass the state.
const Screen = (props) => {
const [color, setColor] = useState("#CCCCCC");
const { navigation } = props //This is important or else UseEffect will be called each time any of the props change
useEffect(() => {
navigation.setParams({ color: color }); // Where its being passed.
}, [color, navigation]);
return (
<>
<Button onPress={() => setColor("#800000")} /> // Change the color state to Maroon
<Button onPress={() => setColor("#FED700")} /> // Change the color state to Gold
</>
)
}
Your Header Component:
const MyHeaderComponent = (props) {
return(
<View style={{ backgroundColor: props.bgColor }} />
)
}
Then you can retrieve this bit in headerRight. Like this:
headerRight:() => <MyHeaderComponent bgColor={route.params.color} />
Note: This method is valid for React Navigation v5. Version 4 has a getParams() function to retrieve the params, but it was dropped in Version 5.
Original Answer
You can create a useState hook in the screen and pass its value into your header component. So, when the header component updates the state, it can be accessed from within the screen where you have defined the state.
you can use setParams() function to set the params you want to use in the header component. Then, use route.params.nameofyourprop to get them in the headerComponent, where you can consume it.
This is to pass params from outside the header to inside of it.
headerRight:() => <MyHeaderRightComponent propname={route.params.propvalue} />
This to to set the Params from outside your header which you can access inside the headerRight component.
const [values, setValue] = useState()
navigation.setParams({propname: value})
This way you can pass state between the header and the screen.
You can also pass the setValue function of the useState in this manner, but it will throw a warning because functions are objects in Javascript and thus its not possible to index them... or something on those lines.

edit screen fields not populating in react-native

I try to open an edit screen for relevant record when user taps the row in the list. I see at debugger all of the props are passing successfully but somehow I cant show them on the screen. I searched a lot and I think the main problem is at onRowPress helper. When I press the row, I see in the debugger all the props have passed correctly. But there is an error that says
Failed prop type: Invalid prop value of type array supplied to
TextInput, expected string.
My question is how should I handle this error.
console.log
onRowPress() {
console.log(this.props.employee);
Actions.employeeEdit({ employee: this.props.employee });
}
I guess you are on "The Complete React Native and Redux Course", if so, i thing you missed:
-1 import Communications from "react-native-communications";
-2
class EmployeeEdit extends Component {
state = { showModal: false };
...
3- Above your render():
onTextPress() {
const { phone, shift } = this.props;
Communications.text(phone, `Your upcoming shift is on ${shift}`);
}
onAccept() {
const { uid } = this.props.employee;
this.props.employeeDelete({ uid });
}
onDecline() {
this.setState({ showModal: false });
}
-4 And finally, your main render() should be:
<Card>
<EmployeeForm />
<CardSection>
<Button onPress={this.onButtonPress.bind(this)}>Save Changes</Button>
</CardSection>
<CardSection>
<Button onPress={this.onTextPress.bind(this)}>Text Schedule</Button>
</CardSection>
<CardSection>
<Button onPress={() => this.setState({ showModal: !this.state.showModal })}>
Fire Employee
</Button>
</CardSection>
<Confirm
visible={this.state.showModal}
onAccept={this.onAccept.bind(this)}
onDecline={this.onDecline.bind(this)}
>
Are you sure you want to delete this?
</Confirm>
</Card>;
It takes time but I solved the problem. Main problem was the error below;
Failed prop type: Invalid prop value of type array supplied to
TextInput, expected string.
I followed all the code step to step and find that the reducer which update the props with the values I pass can't do this. I added a toString() method to the actions.payload.value and everything is ok. You have to pass a string to the Input component.
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EMPLOYEE_UPDATE:
return { ...state, [action.payload.prop]: action.payload.value.toString() };
case EMPLOYEE_CREATE:
return INITIAL_STATE;
case EMPLOYEE_SAVE_SUCCESS:
return INITIAL_STATE;
default:
return state;
}
};