Adam Roberts
Adam Roberts

Reputation: 692

React Context: TypeError: render is not a function

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

Answers (3)

Sharqawy
Sharqawy

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

Vipul Chaudhary
Vipul Chaudhary

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

Estus Flask
Estus Flask

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

Related Questions