I'm trying to use React Context to pass a function to a nested child component, which effectively allows the child to update the parents state when pressed.
The problem is I seem to be getting an error 'TypeError: render is not a function. (In render(newValue), render is an instance of Array' and an error in my console reads: 'Warning: A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it.'
I've looked around at this error and also at documentation but no answers seem to answer my question, I can't quite work out why this isn't working.
EDIT: I should add that there are multiple child components rendered, as they're built from an array of objects
Snippets:
Parent:
class Product extends Component { state = { selected_ind: 0 }; handleContextChange = selected_ind => { this.setState({selected_ind}); }; render() { const contextValue = { data: this.state, handleChange: this.handleContextChange }; //Desconstruct props for ease of use const { attr_data, variant, product_name } = this.props; return ( <Container> <Heading>Product Options</Heading> <IndexContext.Provider value={contextValue}> <OptionTile tileColor='grey' onPress={ () => this.props.navigation.navigate('Variants', { attr_data: attr_data, selected_ind: this.state.selected_ind })} //Replace with named function option="Variant" selected_ind={ this.state.selected_ind } value={ selected_attr.name } /> </IndexContext.Provider> <OptionTile tileColor='grey' option="Quantity" value="1" /> </Container>
Within OptionTile is the child I'd like to use the function within:
const VariantTile = (props) => { return ( <IndexContext.Consumer> {({ handleChange }) => ( <TouchableOpacity onPress={handleChange(props.index)}> <AsyncImage source={ props.img_src } placeholderColor="#fafafa" style={{ flex: 1, width: null, height: 200 }} /> <Text>{ props.var_name }</Text> <Text>{ props.price }</Text> <Text>{ props.sku }</Text> </TouchableOpacity> )}; </IndexContext.Consumer> ) };
And the context component is simple:
const IndexContext = React.createContext(); export default IndexContext;
They all have to match. Separately, there doesn't yet exist a react native adapter for enzyme (see #1436 ), so until there is, enzyme won't work properly with many kinds of react-native components. @ljharb I have updated enzyme & enzyme-adapter-react-16 and tried still getting same error,
After updating all the react-native and jest dependencies for app, test is not running its throwing error like this, Try updating to the latest enzyme and enzyme-adapter-react-16; also, what version of react and react-dom and react-test-renderer are you using? They all have to match.
A context consumer was rendered with multiple children, or a child that isn’t a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it.
As the error states, <Consumer>
should have the only child, and it should be a function. The error appears when it has multiple children, including text nodes.
;
after embedded expression causes the problem. It's not a part of an expression, and making it a part of it would result in syntax error.
It should be:
<IndexContext.Consumer> {({ handleChange }) => ( <TouchableOpacity onPress={handleChange(props.index)}> <AsyncImage source={ props.img_src } placeholderColor="#fafafa" style={{ flex: 1, width: null, height: 200 }} /> <Text>{ props.var_name }</Text> <Text>{ props.price }</Text> <Text>{ props.sku }</Text> </TouchableOpacity> )} </IndexContext.Consumer>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With