Preact: version X: Context HOC - Uncaught TypeError: n.children is not a function

Created on 25 Jul 2019  路  10Comments  路  Source: preactjs/preact

I am trying to create a HOC using Context and I am getting Uncaught TypeError: n.children is not a function

Here is the basic code

index.js:

import { render } from 'preact';
import MyProvider from 'context';
import Button from 'button';

render(
     <MyProvider color="red">
         <Button/>
    </MyProvider>,
    target
);

context.js

import { Component, createContext } from 'preact';

const context = createContext();

export default class MyProvider extends Component {
    render() {
        const { children, ...value } = this.props;
        return (
            <context.Provider value={value}>{children}</context.Provider>
        );
    }
}

// this is the part that is faling
export const withContext = (WrappedComponent) => {
    return class ContextConsumer extends Component {
      render() {
        return (<context.Consumer><WrappedComponent /></context.Consumer>);
      }
    }
}

button.js

import { Component } from 'preact';
import { withContext } from 'context';

class Button extends Component {
    render() {
        return (
            <button>test</button>
        );
    }
}

export default withContext(Buttonn);
invalid

Most helpful comment

These are not equivalent, in the former you are relying on the render function of Consumer to be this:

render() {
  return this.props.render(context)
} 

while in the latter you are relying on this implementation:

render() {
  return this.props.children(context)
} 

All 10 comments

I also tried withContext like this

export const withContext = (WrappedComponent) => {
    return class ContextConsumer extends Component {
        render() {
            return (<context.Consumer><WrappedComponent /></context.Consumer>);
        }
    }
}

Consumer is a render prop so your component should look like:

<Consumer>{(ctx) => <Component ctx={ctx} {...this.props} />}</Consumer>

The error message sais it expects the children to be a function

Hi @JoviDeCroock,

Thanks for getting back. This was actually one of the first things I tried.

export const withContext = (WrappedComponent) => {
    return <context.Consumer>{(ctx) => <WrappedComponent ctx={ctx} {...this.props} />}</context.Consumer>
}

but this gave preact.js?10a9:1 Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('[object Object]') is not a valid name.

This however does seem to work.

export const withContext = (WrappedComponent) => {
    return class ContextConsumer extends Component {
        render() {
            return <context.Consumer>{(ctx) => <WrappedComponent ctx={ctx} {...this.props} />}</context.Consumer>
        }
    }
}

This also used to work in v8 with preact-context

export const withContext = OriginalComponent => props => (
  <context.Consumer render={ctx => <OriginalComponent {...ctx} {...props} />} />
);

Works for me with Preact X, has to be something else on your side: https://codesandbox.io/s/hidden-https-04wf2

ok will look into it.

Are these not equivalent? I was trying the former so wasn't getting success as a render prop.

return <context.Consumer render={(ctx) => <WrappedComponent ctx={ctx} {...this.props} />}></context.Consumer>

return <context.Consumer>{(ctx) => <WrappedComponent ctx={ctx} {...this.props} />}</context.Consumer>

These are not equivalent, in the former you are relying on the render function of Consumer to be this:

render() {
  return this.props.render(context)
} 

while in the latter you are relying on this implementation:

render() {
  return this.props.children(context)
} 

oh, your codesandbox works for me as well. What I was saying above is that this does not work

export const withContext = (WrappedComponent) => {
    return <context.Consumer>{(ctx) => <WrappedComponent ctx={ctx} {...this.props} />}</context.Consumer>
}

It seems that just a simple function like this should work, no?

Because that is not a HOC, that's a regular function for your case you'd need to do this:

export const wrapWithContext = (WrappedComponent) => {
  return (props) => <context.Consumer>{(ctx) => <WrappedComponent ctx={ctx} {...props} />}</context.Consumer>
}

You could also use the hooks API which allows you to just do this const value = useContext(context)

ok great, it was my use of render={ causing issues. Like I mentioned this worked in Preact 8 using preact-context

Thanks for the help looking into this, hopefully this can help others making the transition.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mizchi picture mizchi  路  3Comments

matuscongrady picture matuscongrady  路  3Comments

jescalan picture jescalan  路  3Comments

simonjoom picture simonjoom  路  3Comments

youngwind picture youngwind  路  3Comments