Reputation: 692
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;
Upvotes: 28
Views: 57787
Reputation: 31
This problem happened with me.
I have written:
<SnackBarContext>
<App />
</SnackBarContext>
Instead of
<SnackBarContext.Provider>
<App />
</SnackBarContext.Provider>
I forgot .Provider
:)
Upvotes: 2
Reputation: 51
You may have Imported Context Consumer instead of Provider
Like,
<AuthProvider>
<App />
</AuthProvider>
instead, you may have write
<AuthContext>
<App />
</AuthContext>
Upvotes: 2
Reputation: 222474
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>
Upvotes: 43