I'm trying to implement a button select group/radio button group where i want to play an animation on a button if it's selected. For now i can make the whole button group do the same animation but i want to execute the animation for one button if it's selected instead of the whole group. This is the code what i'm currenly running:
import React, { useState, useEffect } from 'react';
import { Animated, View, Text, Pressable } from 'react-native';
import { CustomRadioButtonStyles } from './CustomRadioButton.styles';
import Icon from 'react-native-vector-icons/AntDesign';
import { ICON } from '../../styles/variables';
export const CustomRadioButton = ({ data, onSelect }) => {
const [userOption, setUserOption] = useState(null);
const selectHandler = (value) => {
onSelect(value);
setUserOption(value);
};
const Sportief = new Animated.Value(1);
const Rollator = new Animated.Value(1);
const Rolstoel = new Animated.Value(1);
const onPressIn = (value) => {
Animated.spring(value, {
toValue: 0.8,
useNativeDriver: true,
}).start();
};
const onPressOut = (value) => {
Animated.spring(value, {
toValue: 1,
useNativeDriver: true,
}).start();
};
const StyleApplier = (value) => {
console.log(value)
switch(value){
case 'Sportief':
SportiefStyle
case 'Rollator':
RollatorStyle
case 'Rolstoel':
RolstoelStyle
}
}
const SportiefStyle = {
transform: [ { scale: Sportief, } ],
};
const RollatorStyle = {
transform: [ { scale: Rollator, } ],
};
const RolstoelStyle = {
transform: [ { scale: Rolstoel, } ],
};
return (
<View>
{data.map((item) => {
return (
<Animated.View key={item.value} style={StyleApplier(item.value)}>
<Pressable
style={
item.value === userOption ? CustomRadioButtonStyles.selected : CustomRadioButtonStyles.unselected
}
key={item.value}
onPressIn={() => onPressIn(item.value)}
onPressOut={() => onPressOut(item.value)}
onPress={() => selectHandler(item.value)}>
<Text style={item.value === userOption ? CustomRadioButtonStyles.selectedText : CustomRadioButtonStyles.unselectedText}> {item.label}</Text>
{item.value === userOption ?
<View style={CustomRadioButtonStyles.iconWrapper}>
<Icon style={CustomRadioButtonStyles.icon} name='check' size={ICON.size.small}/>
</View> : null}
</Pressable>
</Animated.View>
);
})}
</View>
);
}
Dummy data for generating the buttons:
const statusData = [
{
label: 'Ik doe alles nog te voet',
value: 'Sportief'
},
{
label: 'Ik gebruik een rollator',
value: 'Rollator'
},
{
label: 'Ik gebruik een rolstoel/scootmobiel',
value: 'Rolstoel'
}
]
Image of the application
Related
I'm trying to switch this line of code to an arrow function:
<NavigationContainer theme={theme == 'Amber' ? Amber : Tiger}
<NavigationContainer theme={() => {
if (theme == 'Amber') {
return Amber;
} else {
return Tiger;
}
}}>
But for some reason when I perform this change, I get this error: TypeError: undefined is not an object (evaluating 'colors.background')
What's the problem here?
Thanks
Full code for reference:
import React, { useState } from 'react';
import { NavigationContainer, useTheme } from '#react-navigation/native';
import { StatusBar } from 'expo-status-bar';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import * as NavigationBar from "expo-navigation-bar";
import Ionicons from 'react-native-vector-icons/Ionicons';
import CounterScreen from './screens/CounterScreen';
import CustomizeScreen from './screens/CustomizeScreen';
import SettingsScreen from './screens/SettingsScreen';
import MedalsScreen from './screens/MedalsScreen.js';
import { Amber, Tiger } from "../styles/Themes"
import { ThemeContext } from '#rneui/themed';
const counterName = 'Counter';
const customizeName = 'Customize';
const settingsName = 'Settings';
const medalsName = "Medals";
const Tab = createBottomTabNavigator();
NavigationBar.setBackgroundColorAsync("#212121");
export default function MainContainer() {
const [theme, setTheme] = useState('Amber');
const themeData = { theme, setTheme };
return (
<ThemeContext.Provider value={themeData}>
<NavigationContainer theme={() => {
if (theme == 'Amber') {
return Amber;
} else {
return Tiger;
}
}}>
<StatusBar style="auto" />
<Tab.Navigator
initialRouteName={counterName}
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
let rn = route.name;
if (rn === counterName) {
iconName = focused ? 'radio-button-on-outline' : 'radio-button-off-outline';
} else if (rn === customizeName) {
iconName = focused ? 'color-palette' : 'color-palette-outline';
} else if (rn === medalsName) {
iconName = focused ? 'medal' : 'medal-outline';
} else if (rn === settingsName) {
iconName = focused ? 'settings' : 'settings-outline';
}
return <Ionicons name={iconName} size={size} color={color} />
},
tabBarInactiveTintColor: '#aaaaaa',
tabBarLabelStyle: { paddingBottom: 10, fontSize: 10 },
tabBarStyle: { padding: 10, height: 70, borderTopWidth: 1 },
headerStyle: { borderBottomWidth: 1 },
headerTitleAlign: 'center',
headerTitleStyle: { fontSize: 20 },
})}>
<Tab.Screen name={counterName} component={CounterScreen} />
<Tab.Screen name={customizeName} component={CustomizeScreen} />
<Tab.Screen name={medalsName} component={MedalsScreen} />
<Tab.Screen name={settingsName} component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
</ThemeContext.Provider>
);
}
Themes.js
const Amber = {
dark: true,
colors: {
primary: '#FFBF00',
background: '#212121',
card: '#212121',
text: '#FFBF00',
border: '#FFBF00',
notification: '#FFBF00',
},
};
const Tiger = {
dark: true,
colors: {
primary: '#F96815',
background: '#212121',
card: '#212121',
text: '#F96815',
border: '#F96815',
notification: '#F96815',
},
};
export { Amber, Tiger }
looks like its expecting an object, why not try using useMemo or a simple function the generated an object before setting the proprty
const containerTheme = useMemo(() => {
// logic to select theme
if (theme == 'Amber') {
return Amber;
} else {
return Tiger;
}
}, [theme])
<NavigationContainer theme={containerTheme}>
// simple function
const getTheme = () => {
// logic to select theme
if (theme == 'Amber') {
return Amber;
} else {
return Tiger;
}
}
const containerTheme = getTheme();
if your function is expensive check the time
console.time('get theme');
const containerTheme = getTheme();
console.timeEnd('get theme');
can someone tell me why this animation will only run after click the second time on "next" button?
I know its something related to my if checks but if setTocuhed triggers correctly (making the things red) why the animate doesn't?
ive already tried a couple of things but none has worked
import React from 'react';
import { Animated, Text, View, Easing } from 'react-native';
import { withNavigationFocus } from 'react-navigation';
import Input from '../../components/UI/Input';
import registerStyles from './registerStyles';
type props = {
navigation: {
navigate: (arg: string) => void;
};
};
const validation = (input: string) => {
if (input.length < 6) {
return 'min length 6';
}
if (input.length > 30) {
return 'max length: 30';
}
return null;
};
const FullName = (props: props) => {
const [input, setInput] = React.useState<string>('');
const [touched, setTouched] = React.useState<boolean>(false);
const [errorMessage, setErrorMessage] = React.useState<string | null>('');
const viewRef = new Animated.Value(10);
const animate = () => {
return Animated.timing(viewRef, {
toValue: 22,
easing: Easing.elastic(100),
duration: 200,
useNativeDriver: false
}).start(() => {
Animated.timing(viewRef, {
toValue: 10,
duration: 0,
useNativeDriver: false
}).start();
});
};
return (
<Animated.View style={[registerStyles.screen, { paddingLeft: viewRef }]}>
<Input
touched={touched}
value={input}
onChange={(value) => setInput(value)}
autoFocus={true}
returnKeyType='next'
label='Full name'
onSubmitEditing={() => {
const validate = validation(input);
setErrorMessage(() => validate);
if (validate === null) {
setInput(() => '');
setTouched(() => false);
props.navigation.navigate('Email');
return;
}
if (validate !== null) {
animate();
setTouched(() => true);
}
}}
/>
<Text style={registerStyles.error}>{touched && errorMessage}</Text>
</Animated.View>
);
};
export default withNavigationFocus(FullName);
this is the Input component if needed:
const Input = (props: IInput) => {
const errorColor = props.touched ? colors.red : colors.black;
const errorStyles = {
textField: {
borderBottomColor: errorColor
},
label: {
color: errorColor
}
};
return (
<View style={styles.inputContainer}>
<Text style={[styles.label, errorStyles.label]}>{props.label}</Text>
<TextInput
autoCapitalize={props.autoCapitalize}
onChangeText={(value) => props.onChange(value)}
value={props.value}
autoFocus={props.autoFocus}
ref={(input) => {
if (props.inputRef) {
return props.inputRef(input);
}
}}
style={[styles.textField, errorStyles.textField]}
onSubmitEditing={props.onSubmitEditing}
returnKeyType={props.returnKeyType}
blurOnSubmit={false}
/>
</View>
);
};
Insert viewRef inside useRef hook.
const viewRef = React.useRef(new Animated.Value(10)).current;
In this case, React will keep tracking its value and don't lose it after rerendering.
Im working on a cookie clicker type game and Im working on making features like automatic clickers / combo multipliers. I created the start of the game but when I use my auto function it begins bugging after multiple clicks of the clicker. I have linked the code and an example of the bug occurring below.
CODE
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, Image } from "react-native";
import { cases } from "./cases";
import { motion } from "framer-motion";
const io = require("socket.io-client");
const SocketEndpoint = "http://localhost:3000/";
const App = () => {
const [cash, setCash] = useState(0);
const [clicks, setClicks] = useState(0);
const [bot, setBot] = useState(0);
const [open, setOpen] = useState(false);
const [prize, setPrize] = useState(null);
const [connected, setConnected] = useState(false);
const [upgrades, setUpgrades] = useState({auto: false, multi: false})
const [multi, setMulti] = useState(1);
let applied = false;
useEffect(() => {[![enter image description here][1]][1]
const socket = io(SocketEndpoint, {
transports: ["websocket"],
});
socket.on("connect", () => {
setConnected(true)
});
}, [])
useEffect(() => {
if(!applied) {
autoUpgrade();
applied = true;
}
}, [bot, upgrades])
const autoUpgrade = () => {
if (!upgrades.auto) {
return;
}
const inter = setInterval(() => setBot(bot + 1), 1000);
return () => clearInterval(inter);
}
const collectBotIncome = () => {
setCash(cash + bot);
setBot(0);
}
const addMulti = () => {
if (!upgrades.multi) {
return;
}
setMulti(2)
}
const increasePoint = () => {
setClicks(clicks + (1 * multi))
}
const purchaseCase = () => {
setClicks(clicks - 10)
}
const purchaseUpgrade = (upgrade) => {
alert('purchased')
upgrade === 'auto' ? setUpgrades({...upgrades, auto: true}) : null;
upgrade === 'multi' ? setUpgrades({...upgrades, multi: true}) : null;
}
const between = (x, min, max) => {
return x >= min && x <= max;
}
const openCase = () => {
setOpen(true);
purchaseCase();
const ticket = Math.floor(Math.random() * 10000);
console.log('[ROUND] Ticket:' + ticket)
var found = false;
var i;
for (i = 0; i < cases.types[0].items.length; i++) {
var item = cases.types[0].items[i];
if(between(ticket, item.ticketStart, item.ticketEnd)) {
setPrize(item.name)
found = true;
}
}
}
return (
<View style={styles.container}>
<View style={styles.shopMenu}>
<Text style={{color: 'white'}}>Shop</Text>
<Text onClick={() => purchaseUpgrade('auto')} style={{color: 'white'}}>Auto Click Level 1</Text>
<Text onClick={() => purchaseUpgrade('multi')} style={{color: 'white'}}>Click Multiplier Level 1</Text>
</View>
<View style={styles.shopMenu}>
<Text style={{color: 'white'}}>Inventory</Text>
{upgrades.auto
?
<>
<Text style={{color: 'white'}}>{bot}</Text>
<Text onClick={collectBotIncome} style={{color: 'white'}}>Collect Bot Income</Text>
</>
: null
}
</View>
<motion.img
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={increasePoint}
src={require('./src/assets/case.png')} alt="case"
/>
<Text style={{color: 'white'}}>clicks: {clicks ? clicks : "0"}</Text>
<Text style={{color: 'white'}}>connected: {connected ? "true" : "false"}</Text>
{open
? <Text style={{color: 'white'}}>{prize ? prize : 'Failed to get Prize'}</Text>
: <Image
style={clicks >= 10 ? styles.available : styles.unavailable}
source={require('./src/assets/case.png')} alt="case"
onClick={openCase}
/>
}
<Text style={{color: 'white'}}>{cases.types[0].name}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "black",
alignItems: "center",
justifyContent: "center",
},
available: {
display: 'flex',
width: 150,
height: 100,
filter: 'none',
transition: '200ms ease',
marginTop: 50
},
unavailable: {
display: "flex",
width: 150,
height: 100,
filter: 'grayscale(1)',
transition: '200ms ease',
marginTop: 50
},
shopMenu: {
display: "flex",
flexDirection: "column"
}
});
export default App;
PREVIEW
Here is the full code:
import * as React from 'react';
import { View, ScrollView, StyleSheet } from 'react-native';
import {
Appbar,
Searchbar,
List,
BottomNavigation,
Text,
Button,
} from 'react-native-paper';
const AccordionCollection = ({ data }) => {
var bookLists = data.map(function (item) {
var items = [];
for (let i = 0; i < item.total; i++) {
items.push(
<Button mode="contained" style={{ margin: 10 }}>
{i}
</Button>
);
}
return (
<List.Accordion
title={item.title}
left={(props) => <List.Icon {...props} icon="alpha-g-circle" />}>
<View
style={{
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
backgroundColor: 'white',
}}>
{items}
</View>
</List.Accordion>
);
});
return bookLists;
};
const MusicRoute = () => {
const DATA = [
{
key: 1,
title: 'Zain dishes',
total: 21,
},
{
key: 2,
title: 'Sides',
total: 32,
},
{
key: 3,
title: 'Drinks',
total: 53,
},
{
key: 4,
title: 'Aesserts',
total: 14,
},
];
const [data, setData] = React.useState(DATA);
const [searchQuery, setSearchQuery] = React.useState('');
const [sortAZ, setSortAZ] = React.useState(false);
const onChangeSearch = (query) => {
setSearchQuery(query);
const newData = DATA.filter((item) => {
return item.title.toLowerCase().includes(query.toLowerCase());
});
setData(newData);
};
const goSortAZ = () => {
setSortAZ(true);
setData(
data.sort((a, b) => (a.title > b.title ? 1 : b.title > a.title ? -1 : 0))
);
};
const goUnSort = () => {
setSortAZ(false);
setData(DATA);
};
return (
<View>
<Appbar.Header style={styles.appBar}>
<Appbar.BackAction onPress={() => null} />
<Searchbar
placeholder="Search"
onChangeText={onChangeSearch}
value={searchQuery}
style={styles.searchBar}
/>
<Appbar.Action
icon="sort-alphabetical-ascending"
onPress={() => goSortAZ()}
/>
<Appbar.Action icon="library-shelves" onPress={() => goUnSort()} />
</Appbar.Header>
<ScrollView>
<List.Section title="Accordions">
<AccordionCollection data={data} />
</List.Section>
</ScrollView>
</View>
);
};
const AlbumsRoute = () => <Text>Albums</Text>;
const MyComponent = () => {
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'music', title: 'Music', icon: 'queue-music' },
{ key: 'albums', title: 'Albums', icon: 'album' },
]);
const renderScene = BottomNavigation.SceneMap({
music: MusicRoute,
albums: AlbumsRoute,
});
return (
<BottomNavigation
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={renderScene}
/>
);
};
const styles = StyleSheet.create({
appBar: {
justifyContent: 'space-between',
},
searchBar: {
width: '60%',
shadowOpacity: 0,
borderRadius: 10,
backgroundColor: '#e4e3e3',
},
});
export default MyComponent;
Expo Snack Link
There are two weird mechanisms.
First
If I remove sortAZ(true) in goSortAZ() and sortAZ(false) in goUnSort(), the state data stops updating after you press on (1) sort and (2) unsort buttons more than three times.
Second
If I remove DATA array outside the component, sort and unsort buttons does not work/update.
If I do not remove these two, I can sort and unsort the list.
I feel that the code is messy although it achieves the function.
My questions is:
Why adding extra state (sortAZ) helps to update other state (data)?
Just totally remove sortAZ variable (no need to use it unless you somehow want to have a loading status, but since you are not making http requests, that's not necessary) and replace goSortAZ with the following:
Remember to clone the original array in order to create a new copy and then sort that copy.
This is working fine.
const goSortAZ = () => {
setData(
[...data].sort((a, b) => (a.title > b.title ? 1 : b.title > a.title ? -1 : 0))
);
};
i would suggest using the same technique for the unSort method too.
Actually i'm new to React and i'm trying to make a simple barcode scanner which show the scanned barcode in an alert and after pressing "OK" in the alert the user should be able to scan another barcode.
The issue is that the barcode is continuously scanned and when the alert is up it's hiding and showing every second the alert.
I was trying to do something like this to show the alert only once and if OK is pressed to be able to show again the alert but only in case the OK is pressed but that had no effect..
onBarCodeRead = (e) => {
if(!this.alertPresent){
this.alertPresent = true;
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{text: 'OK', onPress: () => this.alertPresent = false;},
],
{cancelable: false},
);
}
}
Here is full code of Barcode.JS
import React, { Component } from 'react';
import { Button, Text, View,Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import BarcodeMask from 'react-native-barcode-mask';
class ProductScanRNCamera extends Component {
constructor(props) {
super(props);
this.camera = null;
this.barcodeCodes = [];
this.alertPresent = false;
this.state = {
camera: {
flashMode: RNCamera.Constants.FlashMode.auto,
}
};
}
onBarCodeRead = (e) => {
if(!this.alertPresent){
this.alertPresent = true;
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{text: 'OK', onPress: () => this.alertPresent = false;},
],
{cancelable: false},
);
}
}
pendingView() {
return (
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text>Waiting</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
defaultTouchToFocus
flashMode={this.state.camera.flashMode}
mirrorImage={false}
onBarCodeRead={this.onBarCodeRead.bind(this)}
onFocusChanged={() => {}}
onZoomChanged={() => {}}
style={styles.preview}
>
<BarcodeMask/>
</RNCamera>
</View>
);
}
}
The trick here is to modify barcodeTypes props with an internal state.
const defaultBarcodeTypes = [// should be all Types from RNCamera.Constants.BarCodeType];
class ProductScanRNCamera extends Component {
state = {
// your other states
barcodeType: '',
barcodeValue: '',
isBarcodeRead: false // default to false
}
onBarcodeRead(event) {
this.setState({isBarcodeRead: true, barcodeType: event.type, barcodeValue: event.data});
}
// run CDU life-cycle hook and check isBarcodeRead state
// Alert is a side-effect and should be handled as such.
componentDidUpdate() {
const {isBarcodeRead, barcodeType, barcodeValue} = this.state;
if (isBarcodeRead) {
Alert.alert(barcodeType, barcodeValue, [
{
text: 'OK',
onPress: () => {
// Reset everything
this.setState({isBarcodeRead: false, barcodeType: '', barcodeValue: ''})
}
}
]);
}
}
render() {
const {isBarcodeRead} = this.state;
return (
<RNCamera {...your other props} barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
<BarcodeMask />
</RNCamera>
)
}
}
A hook version is cleaner
const ProductScanRNCamera = () => {
const [isBarcodeRead, setIsBarcodeRead] = useState(false);
const [barcodeType, setBarcodeType] = useState('');
const [barcodeValue, setBarcodeValue] = useState('');
useEffect(() => {
if (isBarcodeRead) {
Alert.alert(
barcodeType,
barcodeValue,
[
{
text: 'OK',
onPress: () => {
// reset everything
setIsBarcodeRead(false);
setBarcodeType('');
setBarcodeValue('');
}
}
]
);
}
}, [isBarcodeRead, barcodeType, barcodeValue]);
const onBarcodeRead = event => {
if (!isBarcodeRead) {
setIsBarcodeRead(true);
setBarcodeType(event.type);
setBarcodeValue(event.data);
}
}
return (
<RNCamera {...your props}
onBarCodeRead={onBarcodeRead}
barcodeTypes={isBarcodeRead ? [] : defaultBarcodeTypes}>
<BarcodeMask />
</RNCamera>
)
}
use setState in order to set state of component.setState will take the object and update the state of component
check code below
import React, { Component } from 'react';
import { Button, Text, View, Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import BarcodeMask from 'react-native-barcode-mask';
class ProductScanRNCamera extends Component {
constructor(props) {
super(props);
this.camera = null;
this.barcodeCodes = [];
this.showAlert = true;
this.state = {
camera: {
flashMode: RNCamera.Constants.FlashMode.auto,
}
};
}
onBarCodeRead = (e) => {
if (this.state.alertPresent) {
this.setState({ showAlert: false });
Alert.alert(
"Barcode type is " + e.type,
"Barcode value is " + e.data,
[
{ text: 'OK', onPress: () =>console.log('ok') },
],
{ cancelable: false },
);
}
}
pendingView() {
return (
<View
style={{
flex: 1,
backgroundColor: 'lightgreen',
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text>Waiting</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<RNCamera
ref={ref => {
this.camera = ref;
}}
defaultTouchToFocus
flashMode={this.state.camera.flashMode}
mirrorImage={false}
onBarCodeRead={this.onBarCodeRead.bind(this)}
onFocusChanged={() => { }}
onZoomChanged={() => { }}
style={styles.preview}
>
<BarcodeMask />
</RNCamera>
</View>
);
}
}