React Native Changing the State is not re rendering component - react-native

i am trying to use the isEnabled and setIsEnabled useState Hook to update the icon of the play button. When I hit the playbutton, the icon changes appropriately with the useState Hook assumingly triggering a re render, but hitting the restart btn (although i am calling the setIsEnabled) is not triggering a re render of the playbutton icon. Is there a way to force reload it or is there something I'm doing wrong with the useState hook
---- ActionBtn.jsx ------
export const ActionButton = (props: {
name: string;
themeColor: string;
timerReference: any;
}) => {
// tracker for whether timer is active
const [isEnabled, setIsEnabled] = useState(false);
// handle button press for each button
// settings btn
const openSettings = () => {
addHapticFeedback("light");
alert("settings");
};
// play & pause btn
const toggleTimer = () => {
// toggle play or pause button
setIsEnabled(!isEnabled);
addHapticFeedback("light");
if (isEnabled) {
props.timerReference.current.pause();
} else {
props.timerReference.current.play();
}
};
// restart btn
const restartTimer = () => {
// deactivate timer
setIsEnabled(false);
addHapticFeedback("light");
props.timerReference.current.reAnimate();
props.timerReference.current.pause();
};
// define type of action button based on prop passed
if (props.name === "settings") {
return (
<TouchableOpacity
style={[styles.button, { backgroundColor: props.themeColor }]}
onPress={openSettings}
>
<SettingsIcon size={iconSize} fillColor={iconColor} />
</TouchableOpacity>
);
} else if (props.name === "play") {
return (
<TouchableOpacity
style={[styles.button, { backgroundColor: props.themeColor }]}
onPress={toggleTimer}
>
<PlayIcon size={iconSize} fillColor={iconColor} isEnabled={isEnabled} />
</TouchableOpacity>
);
} else if (props.name === "restart") {
return (
<TouchableOpacity
style={[styles.button, { backgroundColor: props.themeColor }]}
onPress={restartTimer}
>
<RestartIcon size={iconSize} fillColor={iconColor} />
</TouchableOpacity>
);
} else {
return <View />;
}
};
----- ICONS.jsx ------
export const PlayIcon = (props: {
size: number;
fillColor: string;
isEnabled: boolean;
}) => {
return props.isEnabled ? (
<Svg width={props.size} height={props.size} viewBox="0 0 40 40">
<Path
d="M25 31.6666C24.0833 31.6666 23.2989 31.3405 22.6467 30.6883C21.9933 30.035 21.6667 29.25 21.6667 28.3333V11.6666C21.6667 10.75 21.9933 9.96553 22.6467 9.31331C23.2989 8.65998 24.0833 8.33331 25 8.33331H28.3333C29.25 8.33331 30.035 8.65998 30.6883 9.31331C31.3406 9.96553 31.6667 10.75 31.6667 11.6666V28.3333C31.6667 29.25 31.3406 30.035 30.6883 30.6883C30.035 31.3405 29.25 31.6666 28.3333 31.6666H25ZM11.6667 31.6666C10.75 31.6666 9.96555 31.3405 9.31333 30.6883C8.65999 30.035 8.33333 29.25 8.33333 28.3333V11.6666C8.33333 10.75 8.65999 9.96553 9.31333 9.31331C9.96555 8.65998 10.75 8.33331 11.6667 8.33331H15C15.9167 8.33331 16.7017 8.65998 17.355 9.31331C18.0072 9.96553 18.3333 10.75 18.3333 11.6666V28.3333C18.3333 29.25 18.0072 30.035 17.355 30.6883C16.7017 31.3405 15.9167 31.6666 15 31.6666H11.6667Z"
fill={props.fillColor}
/>
</Svg>
) : (
<Svg width={props.size} height={props.size} viewBox="0 0 55 55">
<Path
d="M21.84 41.277c-.762.495-1.534.523-2.316.084-.78-.437-1.17-1.113-1.17-2.028V15.667c0-.915.39-1.592 1.17-2.03.782-.438 1.554-.41 2.317.086l18.635 11.833c.686.458 1.03 1.106 1.03 1.944 0 .838-.344 1.486-1.03 1.944L21.841 41.277Z"
fill={props.fillColor}
/>
</Svg>
);
};
export const RestartIcon = (props: { size: number; fillColor: string }) => {
return (
<Svg width={props.size} height={props.size} viewBox="0 0 46 46">
<Path
d="M24.875 22.25L29.5625 26.9375C29.9062 27.2812 30.0781 27.7187 30.0781 28.25C30.0781 28.7812 29.9062 29.2187 29.5625 29.5625C29.2188 29.9062 28.7812 30.0781 28.25 30.0781C27.7188 30.0781 27.2812 29.9062 26.9375 29.5625L21.6875 24.3125C21.5 24.125 21.3594 23.9137 21.2656 23.6787C21.1719 23.445 21.125 23.2031 21.125 22.9531V15.5C21.125 14.9687 21.305 14.5231 21.665 14.1631C22.0238 13.8044 22.4688 13.625 23 13.625C23.5312 13.625 23.9769 13.8044 24.3369 14.1631C24.6956 14.5231 24.875 14.9687 24.875 15.5V22.25ZM23 39.875C19.2188 39.875 15.8281 38.7575 12.8281 36.5225C9.82812 34.2887 7.8125 31.375 6.78125 27.7812C6.625 27.2187 6.68 26.6875 6.94625 26.1875C7.21125 25.6875 7.625 25.375 8.1875 25.25C8.71875 25.125 9.19562 25.2419 9.61812 25.6006C10.0394 25.9606 10.3281 26.4062 10.4844 26.9375C11.2969 29.6875 12.8675 31.9062 15.1962 33.5937C17.5237 35.2812 20.125 36.125 23 36.125C26.6562 36.125 29.7575 34.8512 32.3037 32.3037C34.8512 29.7575 36.125 26.6562 36.125 23C36.125 19.3437 34.8512 16.2419 32.3037 13.6944C29.7575 11.1481 26.6562 9.875 23 9.875C20.8438 9.875 18.8281 10.375 16.9531 11.375C15.0781 12.375 13.5 13.75 12.2188 15.5H15.5C16.0312 15.5 16.4769 15.6794 16.8369 16.0381C17.1956 16.3981 17.375 16.8437 17.375 17.375C17.375 17.9062 17.1956 18.3512 16.8369 18.71C16.4769 19.07 16.0312 19.25 15.5 19.25H8C7.46875 19.25 7.02375 19.07 6.665 18.71C6.305 18.3512 6.125 17.9062 6.125 17.375V9.875C6.125 9.34375 6.305 8.89812 6.665 8.53813C7.02375 8.17938 7.46875 8 8 8C8.53125 8 8.97687 8.17938 9.33687 8.53813C9.69562 8.89812 9.875 9.34375 9.875 9.875V12.4062C11.4688 10.4062 13.4144 8.85937 15.7119 7.76562C18.0081 6.67187 20.4375 6.125 23 6.125C25.3438 6.125 27.5394 6.57 29.5869 7.46C31.6331 8.35125 33.4144 9.55437 34.9306 11.0694C36.4456 12.5856 37.6488 14.3669 38.54 16.4131C39.43 18.4606 39.875 20.6562 39.875 23C39.875 25.3437 39.43 27.5387 38.54 29.585C37.6488 31.6325 36.4456 33.4137 34.9306 34.9287C33.4144 36.445 31.6331 37.6487 29.5869 38.54C27.5394 39.43 25.3438 39.875 23 39.875Z"
fill={props.fillColor}
/>
</Svg>
);
};

You need to use useEffect hook with the restart function. See react docs for details.
code snippet:
import { useEffect } from 'react';
const _isEnabled = false;
useEffect(() => {
setIsEnabled(!_isEnabled);
},[_isEnabled]);
Note: include useEffect hook right after your useState hook at top level. Update value of _isEnabled= !_isEnabled where you want to rerender and don't call setIsEnabled in that function.

I managed to solve it by moving the isEnabled state variable to the parent object and then passing it down to the children as props

Related

React MaterialUi Date Picker throws RangeError Invalid time value

I've tried about everything I could find on the forums etc, re this error, but no success.
Most solutions seem to be
format="DD/MM/YYYY HH:mm"
or
Moment (being locale driven)
I return the selected date into a chip which displays fine, but the 'RangeError.Invalid time value' issue persists into the chip after the correct selected date is rendered in it.
const [effectiveSelectedDate, setEffSelectedDate] = useState();
const handleEffDateChange = (date,name) =>{
setEffSelectedDate(date);
}
export const makeColumns = (columns, language, rawFilters, filters, format_functions) => {
return columns.map((item, i) => {
if (data.className.search("date_time") > -1) {
logic: (.......... ),
display: (filterList, onChange, index, column) => (
<MuiPickersUtilsProvider
utils={DateFnsUtils}
locale={localeMap[i18next.language]} >
<KeyboardDatePicker
fullWidth
variant='inline'
placeholder='yyyy-MM-dd'
format='yyyy-MM-dd'
margin='normal'
id='date-picker-inline'
name='effectiveDate'
value={effectiveSelectedDate}
onChange={handleEffDateChange}
KeyboardButtonProps={{ 'aria-label': 'change date', }}
/>
</MuiPickersUtilsProvider>
)
}
}
);
What am I missing?
Thanks
try this one
const [effectiveSelectedDate, setEffSelectedDate] = useState();
const handleEffDateChange = (date,name) =>{
setEffSelectedDate(date);
}
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
fullWidth
variant='inline'
placeholder='yyyy-MM-dd'
format='yyyy-MM-dd'
margin='normal'
id='date-picker-inline'
name='effectiveDate'
value={effectiveSelectedDate}
onChange={handleEffDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>

how to conditionally render styles in react native component?

I have a custom component that provides the rendered list of data when the data is available.
But when the data is not available in the example case below where data=[], I want to apply a different style to it.
return (
<FollowableArticleListTemplate
style={hasData}
title={ title }
data={ [] }
isFollowing={ isFollowing }
onToggleFollow={ id ? () => toggleSourceFollow(id, isFollowing, title) : null }
setListRef={ this.setListRef }
contentType="source"
/>
);
What I'm looking for is something like this:
style={hasData}
i.e if the data.length > 0
use this style for this component: style={hasData}
otherwise use this style component: style={noData}
This is easily achieved by using the ternary operator. First, you have to conditionally use data using state, not by giving its value directly into the component.
return (
<FollowableArticleListTemplate
style={myData.length > 0 ? hasData : noData}
title={ title }
data={myData}//this is your state
isFollowing={ isFollowing }
onToggleFollow={ id ? () => toggleSourceFollow(id, isFollowing, title) : null }
setListRef={ this.setListRef }
contentType="source"
/>
);
```

How to set an array of multiple data as a state

So basically I want to be able to collect all the values from multiple inputs and set that array as a state. Here is what I am currently working with:
this.state.basket.map(b => {
return (
<View>
<InputSpinner
style={styles.spinnerQty}
max={50}
min={1}
step={1}
rounded={false}
showBorder
colorMax={"#2a292d"}
colorMin={"#2a292d"}
value={b.qty}
onChange={num => {
this.setState({ popUpQty: num });
}}
/>
<View style={styles.hrLine}></View>
</View>
);
});
So I am iterating my basket and setting a spinner with a value from axios output. So there are now multiple InputSpinner with multiple values.
My question is, how can I collect all the values of the onChange, and push it to an array which will eventually become a state. Something like QuantityState: [] would be the values of all the InputSpinner. Hope that made sense. Any help is appreciated. Thanks!
PS. InputSpinner is an npm package from here.
Through this code you can dynamically add/update onChange number on it's particular array instance. num key will be added when a particular onChange trigger so at the end you will get its values which placed on it's index and if key not found that means onChange never triggered for that index
state = {
spinnerData : {},
basket: []
}
this.state.basket.map((b, index) => {
return (
<View>
<InputSpinner
style={styles.spinnerQty}
max={50}
min={1}
step={1}
rounded={false}
showBorder
colorMax={"#2a292d"}
colorMin={"#2a292d"}
value={b.qty}
onChange={num => {
const newbasket = [...this.state.basket];
newbasket[index]["num"] = num;
this.setState({ basket:newbasket });
}}
/>
<View style={styles.hrLine}></View>
</View>
);
});

React Native adjusting font size of listItem dynamically

Hi as shown in the picture you canno't see the full text however I don't want to decrease the fonsize for all other items.
Only if it they're greater that 16 in length.
Can I return the fontSize in my renderTitleStyle method or can I do in within the ListItem props e.g {infoText.length > 16 ? (fontSize: 12) : (fontSize: 32)} However I don't think this works.
renderTitleStyle = item => {
const infoText = item.location_from + item.location_to;
if (infoText.length > 12) {
// Return fontSize ???
}
console.warn(infoText.length);
};
<ListItem
style={styles.activeUser}
onPress={() => this.toggleModalConfirmTrip(item)}
roundAvatar
subtitle={item.user[0].name}
titleStyle={this.renderTitleStyle(item)}
title={`${item.location_from} to ${item.location_to} `}
....[![Example of text not fitting][1]][1]
You should be able to set styles dynamically by passing an array of styles with a style array element that depends on a state or a conditional.
<Text style={[styles.mainStyles, {fontSize: ((infoText && infoText.length) > 16 ? 12 :32) }]}>
{/*other elements*/}
</Text>
In your specific case i would try passing that condictional as property for ListItem Component.
titleStyle={this._renderItemTitleStyle(item)}
dont forget to create the function.
_renderItemTitleStyle = (item) => {
if (item && Object.keys(item).length) {
const infoText = item.location_from + item.location_to;
return {fontSize: (infoText.length > 16 ? 12 :32)}
}
console.warn("param item has no properties");
}

react native function always returns true

In my application, I want to disable an icon conditionally. Here is my code.
<View style={styles.controlsContainer}>
<WorkoutProgressControls
videoPaused={this.state.videoPaused}
onPrevious={() => alert("PREVIOUS MOVEMENT")}
onPause={() => this._onPauseVideo()}
onNext={() => this.getNextMovement()}
disableNext={() => 1 === 1 ? false : true}
/>
</View>
When I set disableNext to true or false, it works properly. But when I tried to change it conditionally or using a function, it always gets disabled.
this is my component which set the disableNext prop.
<DisableableIcon
iconStyles={styles.icon}
resizeMode="contain"
source={Next}
height={35}
width={35}
disabled={props.disableNext}
onIconPressed={props.onNext}
/>
What is the issue here? Why does this work when I set disableNext to true and false, but returns true always when I try to set it conditionally?
You're trying to pass a function as a prop, therefore to make it work either don't use a function or invoke it as a function in your component.
...
disableNext={() => 1 === 1 ? false : true}
...
...
disabled={props.disableNext()}
...
OR
...
disableNext={1 === 1 ? false : true}
...
...
disabled={props.disableNext}
...