Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript compile errors #59

Closed
adventmedia opened this issue Feb 5, 2019 · 20 comments
Closed

Typescript compile errors #59

adventmedia opened this issue Feb 5, 2019 · 20 comments
Labels
needs more info Not enough information to fix this issue

Comments

@adventmedia
Copy link

adventmedia commented Feb 5, 2019

export default withSnackbar(Chat)

throws this error when compiled:
Type error: Argument of type 'typeof Chat' is not assignable to parameter of type 'ComponentType<InjectedNotistackProps>'.

Tech Version
Notistack v0.4.2
React v16.7.0-alpha.2
@material-ui/core v3.7.1
typescript v3.2.2
@iamhosseindhv
Copy link
Owner

iamhosseindhv commented Feb 5, 2019

@adventmedia Would you test this with notistack v4.0.1 and see if it's the new version that is causing the problem or not.

@adventmedia
Copy link
Author

I assume you meant v0.4.1… which I tried; same error.
I added // @ts-ignore above the line in question, it allows the app to compile and so far appears to run as expected. I don't like defeating typescript - defeats the purpose - but hey, if it works…

@iamhosseindhv
Copy link
Owner

@adventmedia Yes that was a typo. It's weird that it's happening even with v0.4.1 as it's been out for about 2 months and no one reported an issue regarding this error. I might be able to help/debug if you share Chat component's code.
You can also have a look into typedef file: index.d.ts

@adventmedia
Copy link
Author

unfortunately not at liberty to share the code. Typescript version is 3.2.2 in case that's relevant.

@iamhosseindhv
Copy link
Owner

Added typescript version to the issue. Will leave this open for a while in case other people wanted to report the same issue.

@iamhosseindhv iamhosseindhv added the needs more info Not enough information to fix this issue label Feb 5, 2019
@omegaphoenix
Copy link

I ran into the same issue and am currently using // @ts-ignore as the temporary fix.

Here is the tslint error and hint:

Argument of type 'typeof EventTable' is not assignable to parameter of type 'ComponentType<InjectedNotistackProps>'.
  Type 'typeof EventTable' is not assignable to type 'ComponentClass<InjectedNotistackProps, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'InjectedNotistackProps' is missing the following properties from type 'EventTableProps': events, locations, teamGroups, timeSlots, and 3 more.ts(2345)

@AlexanderVishnevsky
Copy link

AlexanderVishnevsky commented Feb 19, 2019

Hi @iamhosseindhv . Faced the same problem but when using JS.
As far as I understand the problem: withSnackbar element. I use Apollo and Next.js and wrap all classes in the app like:

class AppWithSnackBar extends React.Component {
    render() {
        const {children} = this.props;

        const childrenWithProps = React.Children.map(children, child =>
            React.cloneElement(child, {
                onPresentSnackbar: this.props.onPresentSnackbar,
                enqueueSnackbar: this.props.enqueueSnackbar
            })
        );

        return <React.Fragment>{childrenWithProps}</React.Fragment>
    }
}

export default withSnackbar(AppWithSnackBar);

Thus, in a class where you need to call a snackbar, you need to add

 props.enqueueSnackbar("You successfully logged in", {
                        variant: "success",
                        action: <Button size="small" color="inherit"><Clear color="inherit"/></Button>
                    });

But when I call a mutation to get a session and user, it causes an endless loop.

Version 0.4.1 works perfect.

What have you changed in the new version? Perhaps withSnackbar logic has changed?

UPDATE.

As far as I saw you add closeSnackbar property to withSnackbar component in 0.4.2. I don't understand why I need a method closeSnackbar. Could you tell something about this situation?

@iamhosseindhv
Copy link
Owner

@AlexanderVishnevsky closeSnackbar, as the name suggests, allows you to dismiss a snackbar programmatically. See doc website example for more info.

I don't have enough info to find the issue in the problem you described. Maybe you (or other people facing this issue) could reproduce the error and provide a sandbox example?

@cwbuecheler
Copy link

Dealing with this same error AND I'm also using functional components instead of class-based, as mentioned here: #29

Here's my TS code, which includes how I've temporarily solved the "use enqueueSnackbar when props isn't available" issue. Not thrilled with the solution but it'll get the job done for now.

import React from 'react';
import { createStyles, withStyles } from '@material-ui/core/styles';
import { withSnackbar } from 'notistack';

import CssBaseline from '@material-ui/core/CssBaseline';

import Header from './Header';
import Sidebar from './Sidebar';

interface Theme {
  mixins: {
    toolbar: any;
  };
  spacing: {
    unit: number;
  };
}

const styles = (theme: Theme) =>
  createStyles({
    root: {
      display: 'flex',
    },
    content: {
      flexGrow: 1,
      padding: theme.spacing.unit * 3,
    },
    toolbar: theme.mixins.toolbar,
  });

interface Props {
  classes: {
    root: string;
    toolbar: string;
    content: string;
  };
  enqueueSnackbar: any;
}

const fetchFakeData = (enqueueSnackbar: (arg0: string) => boolean) => {
  enqueueSnackbar('Success?');
};

const AppLayoutContainer = (props: Props) => {
  const { classes, enqueueSnackbar } = props;
  return (
    <div className={classes.root}>
      <CssBaseline />
      <Header />
      <Sidebar />
      <div className={classes.content}>
        <div className={classes.toolbar} />
        <a onClick={() => fetchFakeData(enqueueSnackbar)}>Test</a>
      </div>
    </div>
  );
};

export default withStyles(styles)(withSnackbar(AppLayoutContainer));

If I change this line:

const AppLayoutContainer = (props: Props) => {

to

const AppLayoutContainer = (props: any) => {

then everything works ... but I'd really like to actually use a TS interface rather than relying on any. If you have any insight, I'd appreciate it! Let me know if there's more info I can provide that might help.

@iamhosseindhv
Copy link
Owner

iamhosseindhv commented Feb 22, 2019

Thanks @cwbuecheler. @AlexanderVishnevsky mentioned that his issue is only appearing in v0.4.2 and v0.4.1 works fine. would you try keeping Props and try with version v0.4.1?

@cwbuecheler
Copy link

@iamhosseindhv - Hmmm. Nope, I'm getting the error with v0.4.1 as well.

Argument of type '(props: Props) => Element' is not assignable to parameter of type 'ComponentType<InjectedNotistackProps>'.
  Type '(props: Props) => Element' is not assignable to type 'FunctionComponent<InjectedNotistackProps>'.
    Types of parameters 'props' and 'props' are incompatible.
      Property 'classes' is missing in type 'InjectedNotistackProps & { children?: ReactNode; }' but required in type 'Props'.ts(2345)
AppLayout.tsx(36, 3): 'classes' is declared here.

It is very possible that I'm not building my Interface correctly as I'm fairly new to TypeScript. :)

@iamhosseindhv
Copy link
Owner

Alright then we'll continue keeping the issue open for more info or ideally some codesandbox.

@cwbuecheler
Copy link

Thanks. If I have a chance this weekend I'll try to get something up in a sandbox. Swamped today!

@cwbuecheler
Copy link

I believe I found a solution for this. Essentially, you have to import the InjectedNotistackProps interface and combine it with your own Props interface in order for the HOC's to behave nicely. Here's some (slightly obfuscated) working code:

import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Typography } from '@material-ui/core';
import { withSnackbar, InjectedNotistackProps } from 'notistack';

import { Foo } from './Foo';
import FooForm from './FooForm';
import { CreateFooMutation, CREATE_FOO } from './CreateFooMutation';
import { FIND_FOOS } from './FindFoosQuery';

interface Props {
  match: {
    params: {
      id: string;
    };
  };
}

type FinalProps = RouteComponentProps & Props & InjectedNotistackProps;

class CreateFoo extends React.Component<FinalProps> {
  render() {
    const {
      match: {
        params: { id },
      },
      enqueueSnackbar,
    } = this.props;

    return (
      <>
        <Typography variant="h1">Create Foo</Typography>

        <CreateFooMutation
          mutation={CREATE_FOO}
          refetchQueries={[{ query: FIND_FOOS }]}
        >
          {(createFoo, { loading, error }) => {
            return (
              <FooForm
                initialValues={{ foo: { title: '' } }}
                onSubmit={async (values, actions) => {
                  try {
                    await createFoo({
                      variables: { foo: values.foo as Foo },
                    });
                  } finally {
                    actions.setSubmitting(false);
                    enqueueSnackbar('Success!');
                  }
                }}
                loading={loading}
                error={error ? error.message : undefined}
              />
            );
          }}
        </CreateFooMutation>
      </>
    );
  }
}

export default withSnackbar(CreateFoo);

The important line is this one:

type FinalProps = RouteComponentProps & Props & InjectedNotistackProps;

Where we're combining the RouteComponentProps interface (pulled from react router), my custom Props interface (declared just above), and the InjectedNotistackProps interface, imported from Notistack. Then we just feed the entire Type to the component.

If you're nesting Higher Order Components (eg: withStyles(styles)(withSnackBar(CreateFoo))), I believe you'll need to take their props into account, too.

@iamhosseindhv
Copy link
Owner

iamhosseindhv commented Feb 25, 2019

@cwbuecheler in that case I'll have to rename InjectedNotistackProps to have a better name such as NotistackProps or withSnackbarProps.

Would be nice if other people who have this issue could confirm the solution.

@AlexanderVishnevsky
Copy link

@iamhosseindhv In my case, the problem was solved by rewriting the mutation from the imperative style to the declarative.
old version:

<Mutation mutation={gql`mutation {createSession} `}>
        {(createSession, {data, loading, error}) => {
            if (error) {
                console.log(JSON.stringify(error));
            }
            if (loading) {
                return <div>Connecting...</div>;
            }
            if (data) {
                sessionStorage.clear();
                props.apolloClient.resetStore().then(() => {
                    let session_token = data.createSession

                    //Notify
                    props.enqueueSnackbar("You successfully logged in", {
                        variant: "success",
                        action: <Button size="small" color="inherit"><Clear color="inherit"/></Button>
                    });
                    Router.push(redirect_url);
                });
                return null;
            } else return (
                drawLogin(createSession, null,)
            )
        }
        }
    </Mutation>

new version:

login() {
        let enqueueSnackbar = this.props.enqueueSnackbar;
        let {t} = this.props;
        return this.props.apolloClient.mutate({
            mutation: gql`
                mutation {
                    createSession
                }
            `
        })
        .then(async (result) => {
            debugger;
            if (result) {
                sessionStorage.clear();
                await this.props.apolloClient.resetStore().then(() => {
                    let session_token = result.data.createSession;
                  
                     //Notify
                    enqueueSnackbar(t("success session"), {
                        variant: "success",
                        action: <Button size="small" color="inherit"><Clear color="inherit"/></Button>
                    });
                    Router.push(redirect_url);
                });
                return null;
            }
        })
        .catch((error) => {
            console.log(JSON.stringify(error));
        });
    }

So, those manipulations do the trick. Works fine with v0.4.2

@ghost
Copy link

ghost commented Feb 27, 2019

This worked for me. ExtendInjectedNotistackProps it on top of your existing components' props.

interface ForgotPasswordDialogProps extends RootState, InjectedNotistackProps {}

@iamhosseindhv
Copy link
Owner

Be aware InjectedSnackbarProps is going to be renamed to withSnackbarProps in the next release.

@createdbyjurand
Copy link

createdbyjurand commented Mar 18, 2019

This works, maybe this will help someone :)

import { withStyles } from '@material-ui/core';
import { withSnackbar } from 'notistack';
import { withSnackbarProps } from 'notistack';

const styles = ({ palette, spacing }: Theme) => createStyles({
  div: {
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  margin: {
    margin: 30,
  },
  title: {
    marginTop: 50,
  },
  footer: {
    textAlign: 'center',
    marginTop: 25,
    marginBottom: 100,
  },
});

interface IProps extends WithStyles<typeof styles>, withSnackbarProps { }

class LoginPage extends Component<IProps, {}> {
  public state = {
    submit: '',
    key: '',
  };

  constructor(props: IProps) {
    super(props);
  }

  public render() {
    return (
      <div>
      </div>
    );
  }
}

export default withStyles(styles)(withSnackbar(LoginPage));

@bradschafer
Copy link

import { WithSnackbarProps, withSnackbar } from 'notistack'

This worked for me in ^0.9.5 release. notice case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs more info Not enough information to fix this issue
Projects
None yet
Development

No branches or pull requests

7 participants