React Native:将数据从 child 组件传回 parent

React Native: passing data back to parent from child component

我正在尝试将从 child 组件获得的数据传递给 parent 组件。

我一直收到一条错误消息“试图分配给 readyonly 属性”。

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

  

我的 child 组件上有一个单选按钮,我希望它的值返回到 parent 组件,因为该数据将在 parent 组件中使用。

child 单选句柄 (OrderCard.js):

const handleRadio = (value) => {
    console.log(value)
    setRadioValue(value)
    if(value === true) {
        props.onChange = true
    } else if (value === false) {
        props.onChange = false
    }
}

在 parent 我映射了组件,因为它就像一张带有按钮的卡片,

                        {orders.map((order, index) => {
                        return (
                            <OrderCard 
                                orderNum={order.order_name} 
                                items={order.order_items} 
                                key={index} 
                                count={index}
                                onChange={(value) => accepted(value)}
                            />
                        )
                    })}

和接受的功能,试图让它出现在控制台中:

    const accepted = (data) => {
    console.log('data: ', data)
    }

完整代码如下

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
}

也许问题出在您尝试在 handleRadio 函数中执行的分配,具体来说,这些行:

this.props.onChange = true
this.props.onChange = false

您已经将函数传递给 onChange 道具,它应该完全按照您的要求执行,此处:

onChange={(value) => accepted(value)}

因此,您可以调用传递给子组件的函数,而不是尝试进行赋值,如下所示:

const handleRadio = (value) => {
    console.log(value)
    setRadioValue(value)
    if(value === true) {
        props.onChange(true)
    } else if (value === false) {
        props.onChange(false)
    }
}

由于这是一个功能组件,'this' 关键字将无法访问。

改用这个:

const handleRadio = (value) => {
    console.log(value)
    setRadioValue(value)
    if(value === true) {
        props.onChange(true)
    } else if (value === false) {
        props.onChange(false)
    }
}