Reputation: 73
I'm trying to pass data I got from the child component to the parent component.
I keep on getting an error saying "Attempted to assign to readyonly property."
TypeError: Attempted to assign to readonly property.
at node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in registerError
at node_modules\react-native\Libraries\LogBox\LogBox.js:60:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:34:4 in console.error
at node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:104:6 in reportException
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:172:19 in handleException
at node_modules\react-native\Libraries\Core\setUpErrorHandling.js:24:6 in handleError
at node_modules\expo-error-recovery\build\ErrorRecovery.fx.js:12:21 in ErrorUtils.setGlobalHandler$argument_0
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:294:29 in invoke
at node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
at node_modules\regenerator-runtime\runtime.js:155:27 in invoke
at node_modules\regenerator-runtime\runtime.js:165:18 in PromiseImpl.resolve.then$argument_0
at node_modules\react-native\node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
at node_modules\react-native\node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:123:14 in _callTimer
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:177:14 in _callImmediatesPass
at node_modules\react-native\Libraries\Core\Timers\JSTimers.js:437:30 in callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:388:6 in __callImmediates
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:132:6 in __guard$argument_0at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:131:4 in flushedQueue
I have a radio button on my child component, and I want it's value to go back to the parent component since that data will be used in the parent component.
child radio handle (OrderCard.js):
const handleRadio = (value) => {
console.log(value)
setRadioValue(value)
if(value === true) {
props.onChange = true
} else if (value === false) {
props.onChange = false
}
}
on the parent I mapped the component since it is like a card with buttons,
{orders.map((order, index) => {
return (
<OrderCard
orderNum={order.order_name}
items={order.order_items}
key={index}
count={index}
onChange={(value) => accepted(value)}
/>
)
})}
and the accepted function, tried to make it appear in console:
const accepted = (data) => {
console.log('data: ', data)
}
full code below
parent:
import { Box, Button, Center, Heading, HStack, Text, VStack, Flex, View, ScrollView, Spinner } from 'native-base';
import React, { useState, useEffect } from 'react';
import { StyleSheet } from 'react-native';
import { OrderCard } from '../../components/scanners/batch/assemblyList/OrderCard';
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';
const BatchAssemblyList = ({ route, navigation }) => {
const { batchNumber } = route.params;
const batchNum = batchNumber;
// console.log(batchNum)
const token = AsyncStorage.getItem('token');
const [ isLoading, setIsLoading ] = useState(true);
const [ orders, setOrders ] = useState([]);
const [ accepted, setAccepted ] = useState(null);
const getOrders = async () => {
let config = {
headers: {
'Authorization': 'Bearer ' + token
}
}
let payload = {
batch_no: batchNum
}
await axios.post(`someapi`, payload, config)
.then(res => {
console.log(res.data)
setOrders(res.data)
setIsLoading(false)
})
.catch(err => {
console.log(err)
})
}
useEffect(() => {
getOrders();
}, []);
useEffect(() => {
console.log(accepted)
}, [accepted]);
if (isLoading) {
return (
<View flex='1' bgColor='#FFEFF0'>
<VStack>
<Flex direction='row' style={styles.header}>
<HStack flex='1' flexDirection='row' justifyContent='flex-start'>
<Heading size='sm' marginLeft='2' alignSelf='center'>Batch {batchNum}</Heading>
</HStack>
</Flex>
</VStack>
<VStack justifyContent="center" alignItems="center">
<Center>
<Spinner size='lg' color='indigo.500' accessibilityLabel="Loading orders" />
<Heading color='indigo.500' fontSize="md">
🧾 Getting Orders 🧾
</Heading>
</Center>
</VStack>
</View>
)
}
return (
<View flex='1' bgColor='#FFEFF0'>
<VStack>
<Flex direction='row' style={styles.header}>
<HStack flex='1' flexDirection='row' justifyContent='flex-start'>
<Heading size='sm' marginLeft='2' alignSelf='center'>Batch {batchNum}</Heading>
</HStack>
<HStack flex='1' flexDirection='row' justifyContent='flex-end'>
<Button size='sm' marginRight='2' variant='unstyled' style={styles.btnDiscard}><Text color='white'>Discard</Text></Button>
<Button size='sm' variant='unstyled' style={styles.btnFinish}><Text color='white'>Finish</Text></Button>
</HStack>
</Flex>
</VStack>
<VStack>
<Center marginBottom='24'>
<ScrollView>
{orders.map((order, index) => {
return (
<OrderCard
orderNum={order.order_name}
items={order.order_items}
key={index}
count={index}
onChange={(value) => setAccepted(value)}
/>
)
})}
</ScrollView>
</Center>
</VStack>
</View>
)
}
const styles = StyleSheet.create({
header: {
backgroundColor: '#FFF',
marginTop: 12,
marginBottom: 12,
marginHorizontal: 12,
padding: 8,
borderRadius: 5,
},
btnDiscard: {
backgroundColor: '#E63B3B',
width: 70,
},
btnFinish: {
backgroundColor: '#30AB28',
width: 70,
}
})
export {
BatchAssemblyList
}
child:
import React, { useEffect, useState, useRef } from 'react';
import { Box, HStack, Switch, Text, VStack, Flex, Radio, Stack, Divider, Checkbox, Hidden, Icon } from 'native-base';
import { StyleSheet } from 'react-native';
import { responsiveFontSize, responsiveScreenWidth } from 'react-native-responsive-dimensions';
import { Entypo } from '@expo/vector-icons';
const OrderCard = (props) => {
//refactor code use layouts
const items = props.items;
const orderNum = props.orderNum;
const count = props.count;
const [ radioValue, setRadioValue ] = useState('');
const [ orderItems, setOrderItems ] = useState([]);
const [ acceptRadio, setAcceptRadio ] = useState(true);
const [ radioKey, setRadioKey ] = useState(0);
const orders = () => {
if (items.length > 1) {
items.map((item, index) => {
let items = {
name: item.name,
order_id: item.order_id,
accepted: false
};
setOrderItems(prevState => [...prevState, items]);
// console.log(orderItems);
})
} else if (items.length === 1) {
setOrderItems(prevState => [...prevState, {
name: items[0].name,
order_id: items[0].order_id,
accepted: true
}]);
}
}
useEffect(() => {
orders();
}, [items])
useEffect(() => {
console.log('uef ',orderItems.length)
checkRadio();
}, [orderItems])
const handleAccept = (index) => {
setOrderItems(prevState => {
let newState = [...prevState];
newState[index].accepted = !newState[index].accepted;
return newState;
});
}
const checkRadio = () => {
if(orderItems.length > 1) {
let acceptedOrders = orderItems.filter(item => {
if(item.accepted) {
return item
}
})
console.log(acceptedOrders)
if(acceptedOrders.length === orderItems.length) {
console.log('all orders accepted')
setRadioKey(radioKey + 1)
setAcceptRadio(false)
} else {
console.log('not accepted')
setRadioKey(radioKey + 1)
setAcceptRadio(true)
}
} else if (orderItems.length === 1) {
setRadioKey(radioKey + 1)
setAcceptRadio(false)
}
}
const handleRadio = (value) => {
console.log(value)
setRadioValue(value)
if(value === true) {
this.props.onChange = true
} else if (value === false) {
this.props.onChange = false
}
}
const itemList = () => {
if (items.length > 1) {
return items.map((item, index) => {
if(item.variant === 'Simple' || item.variant === 'simple' || item.variant === undefined) {
return (
<VStack key={index}>
<HStack flex='1' flexDirection='row' marginY='6'>
<HStack flex='1' flexDirection='row' justifyContent='flex-start' alignContent='center'>
<VStack>
<Text>{item.name}</Text>
</VStack>
</HStack>
<HStack flex='1' flexDirection='row' justifyContent='flex-end' alignSelf='center'>
<Checkbox colorScheme='green' value={item.name} onChange={() => handleAccept(index)} accessibilityLabel='check if assembled' key={index}/>
</HStack>
</HStack>
<Divider />
</VStack>
)
}
return (
<VStack key={index}>
<HStack flex='1' flexDirection='row' marginY='6'>
<HStack flex='1' flexDirection='row' justifyContent='flex-start' alignContent='center'>
<VStack>
<Text>{item.name}</Text>
<Text>{item.variant}</Text>
</VStack>
</HStack>
<HStack flex='1' flexDirection='row' justifyContent='flex-end' alignSelf='center'>
<Checkbox colorScheme='green' value={item.name} onChange={() => handleAccept(index)} accessibilityLabel='check if assembled' key={index}/>
</HStack>
</HStack>
<Divider />
</VStack>
)
})
} else {
return items.map((item, index) => {
if(item.variant === 'Simple' || item.variant === 'simple' || item.variant === undefined) {
return (
<VStack key={index}>
<HStack flex='1' flexDirection='row' marginY='6'>
<HStack flex='1' flexDirection='row' justifyContent='flex-start' alignContent='center'>
<VStack>
<Text>{item.name}</Text>
</VStack>
</HStack>
<Hidden>
<HStack flex='1' flexDirection='row' justifyContent='flex-end' alignSelf='center'>
<Checkbox value={item.name} onChange={() => handleAccept(index)} accessibilityLabel='check if assembled' isChecked isDisabled key={index}/>
</HStack>
</Hidden>
</HStack>
<Divider />
</VStack>
)
}
return (
<VStack key={index}>
<HStack flex='1' flexDirection='row' marginY='6'>
<HStack flex='1' flexDirection='row' justifyContent='flex-start' alignContent='center'>
<VStack>
<Text>{item.name}</Text>
<Text>{item.variant}</Text>
</VStack>
</HStack>
<Hidden>
<HStack flex='1' flexDirection='row' justifyContent='flex-end' alignSelf='center'>
<Checkbox value={item.name} onChange={() => handleAccept(index)} accessibilityLabel='check if assembled' isChecked isDisabled key={index}/>
</HStack>
</Hidden>
</HStack>
<Divider />
</VStack>
)
})
}
}
return (
<Box bg='#FFF' paddingY='6' paddingX='4' borderRadius='5' marginBottom='4'>
<HStack>
<VStack>
<HStack style={styles.orderHeader}>
<VStack>
<Text alignSelf='center' mr='auto' style={styles.orderNum} bold>Order {count + 1}</Text>
<Text alignSelf='center' style={styles.orderNum} bold>{orderNum}</Text>
</VStack>
<HStack style={styles.orderRadio}>
<Radio.Group name="orderRadio" accessibilityLabel="Accept or Reject Order" value={radioValue} onChange={nextValue => {
handleRadio(nextValue);
}} key={radioKey} >
<HStack>
<Radio
style={styles.rejectRadio}
size='lg'
value={false}
mx={1}
colorScheme='red'
icon={<Icon as={<Entypo name="squared-cross" size={24} />}/> }
>
<Text style={{ display: 'none' }}>Reject</Text>
</Radio>
<Radio
style={styles.acceptRadio}
size='lg'
value={true}
mx={1}
colorScheme='green'
icon={<Icon as={<Entypo name="squared-cross" size={24} />}/> }
isDisabled={acceptRadio}
>
<Text style={{ display: 'none' }}>Accept</Text>
</Radio>
</HStack>
</Radio.Group>
</HStack>
</HStack>
<Divider />
<VStack>
{
itemList()
}
</VStack>
</VStack>
</HStack>
</Box>
);
}
const styles = StyleSheet.create({
orderHeader: {
width: responsiveScreenWidth(85),
marginBottom: 6
},
orderNum: {
fontSize: responsiveFontSize(1.8),
},
orderRadio: {
marginLeft: 'auto',
},
orderItems: {
backgroundColor: 'blue',
},
radioLabel: {
fontSize: responsiveFontSize(1.4),
fontWeight: 'bold',
marginLeft: 2
},
acceptRadio: {
borderColor: 'green',
borderRadius: 0,
},
rejectRadio: {
borderColor: 'red',
borderRadius: 0,
}
});
export {
OrderCard
}
Upvotes: 0
Views: 324
Reputation: 548
Maybe the problem is with the assignments you're trying to do in your handleRadio function, specifically, these lines:
this.props.onChange = true
this.props.onChange = false
You're already passing a function to the onChange prop, which should do exactly what you want, here:
onChange={(value) => accepted(value)}
So, instead of trying to do an assignment, you can just call the function you passed to the child component, like so:
const handleRadio = (value) => {
console.log(value)
setRadioValue(value)
if(value === true) {
props.onChange(true)
} else if (value === false) {
props.onChange(false)
}
}
Upvotes: 1
Reputation: 127
Since this is a functional component, 'this' keyword won't be accessible.
Use this instead:
const handleRadio = (value) => {
console.log(value)
setRadioValue(value)
if(value === true) {
props.onChange(true)
} else if (value === false) {
props.onChange(false)
}
}
Upvotes: 0