0

I've been out of the React game for awhile. Come back and I'm trying to implement the Material UI library which has been rewritten with Hooks.

It seems to be extremely confusing + spagetti code in my eyes.

I simply want to reference a function so I can toggle the drawer, how can I do this?

// Old class
export default class DefaultContainer extends Component<ViewProps, any> {

    render() {
        return (
            <View>

                <MainAppBar
                    onPress={() => this.onMenuPressed()}
                />

                {this.props.children}

                <MainDrawer
                    ref={'drawer'}
                />
            </View>
        );
    }

    onMenuPressed = () => {
       // TODO The bit that isn't working
       (this.refs['drawer'] as Drawer).handleToggle()
    }
}

Now the new material UI drawer

// New Drawer (3x more code now..)
const useStyles = makeStyles({
    list: {
        width: 280,
    },
    fullList: {
        width: 'auto',
    },
})

type Props = {
}

function MainDrawer(props: Props, ref: any) {

    const classes = useStyles();
    const [state, setState] = React.useState({
        left: false,
    });

    const toggleDrawer = () => (
        event: React.KeyboardEvent | React.MouseEvent,
    ) => {
        if (
            event.type === 'keydown' &&
            ((event as React.KeyboardEvent).key === 'Tab' ||
                (event as React.KeyboardEvent).key === 'Shift')
        ) {
            return;
        }

        setState({ ...state, left: true });
    };

    const inputRef = useRef();
    useImperativeHandle(ref, () => {
        toggleDrawer()
    });

    const sideList = () => (
        <div
            className={classes.list}
            role="presentation"
            onClick={toggleDrawer()}
            onKeyDown={toggleDrawer()}
        >
            <List>
                <ListItem button key={'drawer_item'}>
                    <ListItemIcon><GroupIcon /></ListItemIcon>
                    <ListItemText primary={'Test Item'} />
                </ListItem>
            </List>
        </div>
    );

    return (
        <div>
            <Button onClick={toggleDrawer()}>Open Left</Button>
            <Drawer open={state.left} onClose={toggleDrawer()}>
                {sideList()}
            </Drawer>
        </div>
    );
}

export default forwardRef(MainDrawer);

1 Answer 1

1

I'm struggling to understand why you need to invoke a function from inside MainDrawer rather than just leveraging the use of props e.g.

Container

export default function DefaultContainer(props: ViewProps) {
  const [drawerOpen, setDrawerOpen] = React.useState(false);
  // assuming it's a toggle?
  const toggleMenu = React.useCallback(() => setDrawerOpen(open => !open));
  return (
    <View>
        <MainAppBar onPress={toggleMenu} />
        {this.props.children}
        <MainDrawer open={drawerOpen} />
    </View>
  )
}

MainDrawer

function MainDrawer(props: Props) {
  const [open, setOpen] = React.useState(props.open);
  ...
  const toggleDrawer = React.useCallback(() => setOpen(open => !open));
  return (
    <div>
        <Button onClick={toggleDrawer}>Open Left</Button>
        // use prop to determine whether drawer is open or closed
        <Drawer open={open} onClose={toggleDrawer}>
          {sideList()}
        </Drawer>
    </div>
  );
}
Sign up to request clarification or add additional context in comments.

10 Comments

If I do this I get an error saying hooks cannot be used outside of a function. It's a large project with many existing components; I need to convert them all to functions now?
@OliverDixon sounds like you are attempting to use a hook in a class component? I've converted your DefaultContainer over to a function component. It shouldn't have much affect on anything doing that, however, if you need it to stay a class then you can just adjust to suit the state management.
@OliverDixon no you are trying to mix a class component with a function component (which is fine), you 100% cannot use hooks in a class component, my code does not do that so the error you are getting can't be using the code as written.
What would the example look like if the DefaultContainer was a class?
@OliverDixon much the same as your original class, only leveraging setState rather than trying to call a function in your nested component. Is there some reason you can't use the converted function component in the example? If the code you have shown is complete then it should be a like for like swap out
|

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.