Функция React не переходит от функционального к функциональному компоненту - PullRequest
2 голосов
/ 06 мая 2020

Я довольно привык к классовым компонентам и теперь пытаюсь больше привыкнуть к хукам, поэтому я погружаюсь в функциональные компоненты.

Возникла серьезная проблема, так как я не смог передать функцию вниз от одного функционального компонента к его дочернему функциональному компоненту.

В родительском компоненте:

import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';
import { MenuList, MenuItem } from '@material-ui/core';
import { withCookies, Cookies } from 'react-cookie';
import { ExpandMore } from '@material-ui/icons';
import PropTypes from 'prop-types';
import styles from '../styles/nav.module.scss';
import NavDrawer from './navDrawer';

const activeStyle = {
   color: '#445565',
   backgroundColor: '#de1b',
   borderColor: '#ced4da',
};

const NavMobile = (props) => {
const [isOpenProductsMenu, setIsOpenProductsMenu] = useState(false);
const [isOpenServicesMenu, setIsOpenServicesMenu] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);

const { cookies, categories } = props;
const main = process.env.ROOT_CATEGORIES.split(',');
useEffect(() => {
    if (cookies.get('roles')) {
        const roles = cookies.get('roles');
        setIsAdmin(!!roles.includes('ROLE_ADMIN') || roles.includes('ROLE_SUPERADMIN'));
    }
});
const toggleProductsMenu = (isOpen) => {
    if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
        return;
    }
    setIsOpenProductsMenu(isOpen);
};
const toggleServicesMenu = (isOpen) => {
    if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
        return;
    }
    setIsOpenServicesMenu(isOpen);
};
return (
    <MenuList className={styles.navMobile} style={{ padding: '0 10px 0 0' }}>
        <MenuItem disableGutters>
            <Link to="/" activeStyle={activeStyle}>Home</Link>
        </MenuItem>
        <MenuItem disableGutters>
            <Link to="/search" activeStyle={activeStyle}>Search</Link>
        </MenuItem>
        <MenuItem
            onMouseEnter={() => toggleProductsMenu(true)}
            onMouseLeave={() => toggleProductsMenu(false)}
            className={styles.overrideItem}
            disableGutters
        >
            <NavDrawer
                type={main[0]}
                open={isOpenProductsMenu}
                categories={categories}
                toggleMenu={toggleProductsMenu}
            />
            <Link to="/" onClick={(event) => event.preventDefault}>
                <div className={styles.itemWrap}>
                    {main[0]}
                    <ExpandMore />
                </div>
            </Link>
        </MenuItem>
        <MenuItem
            onMouseEnter={() => toggleServicesMenu(true)}
            onMouseLeave={() => toggleServicesMenu(false)}
            className={styles.overrideItem}
            disableGutters
        >
            <NavDrawer
                type={main[1]}
                open={isOpenServicesMenu}
                categories={categories}
                toggleMenu={toggleServicesMenu}
            />
            <Link to="/" onClick={(event) => event.preventDefault}>
                <div className={styles.itemWrap}>
                    {main[1]}
                    <ExpandMore />
                </div>
            </Link>
        </MenuItem>
        {isAdmin ?
            (
                <MenuItem disableGutters>
                    <Link to="/admin" activeStyle={activeStyle}>Admin</Link>
                </MenuItem>
            ) : null}
    </MenuList>
);
};

NavMobile.propTypes = {
    cookies: PropTypes.instanceOf(Cookies).isRequired,
   categories: PropTypes.array.isRequired,
};

 export default withCookies(NavMobile);

Но если я попытаюсь получить доступ к свойствам внутри дочернего компонента, как показано ниже, toggleMenu функция не будет в выводе;

И, конечно, выдаст type error.

export default function NavDrawer(props) {
 const {
    type,
    open,
    categories,
    toggleMenu,
 } = props;
 console.log('props drawer', props);

             <Drawer
            id={`parentDrawer-${type}`}
            onMouseEnter={() => setIsOpenSubDrawer(true)}
            onMouseLeave={() => setIsOpenSubDrawer(false)}
            className={classes.drawer}
            variant="temporary"
            open={open}
            close={toggleMenu(false)}

console output

Что мне не хватает?

Ответы [ 2 ]

1 голос
/ 06 мая 2020

Объект события в toggleProductsMenu и toggleServicesMenu не определен.

1 голос
/ 06 мая 2020

Итак, прежде чем я укажу на два возможных решения, важно отметить, что вы используете метод render в вызове родительской функции, и что методы render используются только в функциях класса, а не в функциях без состояния.

Если вы хотите использовать функцию без сохранения состояния, вам следует использовать return.

Обратите внимание на разницу в документах

И вот скриншоты :

Stateless Component

Class Component

Теперь о решении, Stateless Functions не должны есть методы, и добавление их обычно считается плохой практикой. Я понимаю, что вы хотите познакомиться с компонентами, не относящимися к классу, но нет ничего плохого в использовании их сочетания, и обычно так и должно быть. Если вы хотите добавить методы, вы должны использовать Class Component и делать это как this :

class NavMobile extends Component {
  constructor(props) {
    super(props);   
    this.state = {
     // Empty for now
    }
  }


  toggleMenu = () => {
    // Your code
  }

  render() {
    return (
      // More code 

      <Navbar toggleMenu={this.toggleMenu} />

     // More code
    )
  }
}

Причина, по которой это считается плохой практикой, заключается в том, что функция toggleMenu() будет переопределяться каждый раз при вызове компонента.

Если вы действительно хотите go вперед с этим, то вам следует объявить функцию вне функции компонента, поэтому вы объявляете функцию только один раз и используете та же ссылка.

Вы должны попробовать что-то в этом роде:

const toggleMenu = (isOpen) => { ... };

И называть это в компоненте следующим образом

const NavMobile = (props) => {
  return (
    // Your code
    <NavBar toggleMenu={toggleMenu.bind(null, propsYouWantToPass} />
  )
}

Наконец, похоже, что вы ' ищем объект события внутри функции, например, (event.type === 'keydown'), но такое событие не передается, поскольку в дочернем компоненте NavBar вы вызываете его с аргументом false.

Сообщите мне, поможет ли это .

PS: Мой комментарий в самом начале о render против return был нацелен на ваш код до того, как вы внесли изменения. Я все равно оставлю это здесь для потомков.

...