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

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

Related

Updating nested state in React Native when mapping over data

I am mapping over state which displays 3 radio buttons and I only want 1 out of the 3 to be selected at one time and show the selected radio button style - so just a standard behaviour.
The first radio button - isChecked is defaulted to true and I then want to be able to switch the selected styles onPress of the other radio buttons which displays an inner filled circle.
I am getting confused on how to handle the isChecked as true for only the selected Radio Button onPress. I believe I should be using the index of the map to update the state but im unsure on how to go about it.
const [option, setOption] = useState([
{ permission: 'Standard User', isChecked: true },
{ permission: 'Approver', isChecked: false },
{ permission: 'Administrator', isChecked: false },
]);
const RadioButton = ({ checked, onCheck }) => (
<StyledRadioButton onPress={onCheck}>
{checked && <SelectedRadioButton />}
</StyledRadioButton>
);
{option.map(({ isChecked }, i) => (
<RadioButton
onCheck={() =>
setOption(
...prev => {
!prev.isChecked;
},
)
}
checked={isChecked}
/>
))}
Your state is an array of objects which hold a boolean flag. If the user checks a checkbox, then this flag should be set to true while all the others should be set to false. The setter of the state receives an array again, thus we could use the map function again as well as the index argument as you have suggested yourself in your question.
{option.map(({ isChecked }, i) => (
<RadioButton
onCheck={() =>
setOption(
prev => prev.map((opt, index) => ({...opt, isChecked: i === index ? true : false}))
)
}
checked={isChecked}
/>
))}

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.

React native flatlist rerender

I'm working on a flatlist that has complex child that cause expensive rerender, I need to optimize that but I'm not able to stop the rerendering with useMemo, please help me to go through this.
Here my list code:
<FlatList
data={thePosts}
extraData={thePosts}
keyExtractor={(item, index) => index.toString()}
removeClippedSubviews={true}
maxToRenderPerBatch={5}
updateCellsBatchingPeriod={30}
initialNumToRender={11}
windowSize={5}
refreshing={isRefreshing}
onRefresh={handleOnRefresh}
onEndReached={isLoading ? () => null : () => getPosts("more")}
onEndReachedThreshold={0.1}
renderItem={memoizedPost}
//renderItem={renderThePost}
ItemSeparatorComponent={renderThePostSep}
ListFooterComponent={renderThePostListFooter}
/>
here the renderPost:
const renderThePost = (props) => {
let post = props.item;
if (post[0].type == "share") {
return (
<TheSharedPost thePost={post} />
);
} else {
return <ThePost thePost={post} />;
}
};
I've tried to use memoization like this:
const memoizedPost = useMemo(() => renderThePost, []);
Now the problem is, the empty array as useMemo argument I think that only accept the first render but not working, I've tried to use [item.someProperty] but I'm not able to recognize item in the argument (item is not defined)
I've also used useCallback but still no luck, a lot o rerendering happen. Please help me to fix this. Tnz
you can use React.memo to avoid rendering of flatlist items
function TheSharedPost(props) {
/* render using props */
}
export default React.memo(TheSharedPost);
function ThePost(props) {
/* render using props */
}
export default React.memo(ThePost);

React-native Formik setFieldValue

Here is a simplified version of my code.
Notice the setFieldValue_ and this.setFieldValue_ = setFieldValue;
This code works fine, I'm able to get the output when submit button is clicked.
I'm actually wondering if this is the right way to do it? If not, can you point me to the right direction? Also what is this method called? (assigning class variable to some function and use it within another function)
class MyComponent extends React.Component {
setFieldValue_;
someFunction() {
this.setFieldValue_("name", value);
}
render() {
return (
<Formik
initialValues={{
something: ""
}}
onSubmit={values => console.log(values)}
>
{({
setFieldValue,
}) => {
this.setFieldValue_ = setFieldValue;
<ThirdPartyCustomComponent onChange={this.someFunction} />
}}
</Formik>
}
}
I would personally have the onChange simply call formik set field value there and then rather than using different functions. Strictly speaking you don't want to set the value like that because every re-render is setting the value again.
I would also recommend looking at custom formik inputs using the useField hook - https://jaredpalmer.com/formik/docs/api/useField. This will allow you to write a small wrapper around your third party component and formik. Noticing you have used a class based component you may want to do some short reading into react hooks before throwing yourself into using useField.
Docs example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className='error'>{meta.error}</div>
) : null}
</>
);
};

How to initiate props when rendering Component?

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