28

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;
4
  • <VariantTile> is a child of <OptionTile>? Commented Jan 22, 2019 at 9:11
  • Could you provide a minimal reproducible working example? It's hard to fill in the missing pieces at the moment. Commented Jan 22, 2019 at 9:15
  • VariantTile is a child of VariantScreen, OptionTile navigates to the VariantScreen navigation stack Commented Jan 22, 2019 at 9:15
  • Sounds as if the Consumer isn't sitting inside the Provider scope, hence cannot access it. Could you share minimal repository? Commented Jan 22, 2019 at 9:48

3 Answers 3

43

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>
Sign up to request clarification or add additional context in comments.

4 Comments

This kind of bug is really hard to fix. Especially when the error message does provide any useful information.
Unfortunately, it comes down to experience. Once you witnessed this problem or have a good idea about the way jsx and react renderer work, you know what to look for. I believe it happened to me already and I had aha moment.
This is amazing. Is there any "strict mode" that deals beforehand with ";" ? It's just syntax, not even types...
@nicolas No, this is just a human mistake that can't be addressed by the framework more specific than error message does. It's about the correct understanding of JSX syntax. Text outside {} is HTML layout, there could be random text instead of ; with the same result, it's not allowed in <Consumer> children. There's no place for ; any way because anything inside {} is JS expression, while semicolon is used with statements, not expressions.
2

You may have Imported Context Consumer instead of Provider

Like,

 <AuthProvider>
    <App />
</AuthProvider>

instead, you may have write

  <AuthContext>
    <App />
  </AuthContext>

Comments

2

This problem happened with me.

I have written:

  <SnackBarContext>
    <App />
  </SnackBarContext>

Instead of

  <SnackBarContext.Provider>
    <App />
  </SnackBarContext.Provider>

I forgot .Provider :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.