ReactJS и Typescript - дочерние компоненты: получают дочерние реквизиты и обновляют sh дочерние элементы при изменении родительского состояния - PullRequest
0 голосов
/ 29 мая 2020

Мне нужна помощь по ReactJS и коду Typescript с использованием material-ui.

Я хочу переделать систему вкладок, предложенную по следующей ссылке, создав собственные имена компонентов: https://material-ui.com/components/tabs/#simple -tabs .

Вот код, который я сделал: https://codepen.io/Answerrer/pen/wvKLLzg.

interface MyTabPanelProperties {
  label: string;
  value: number;
  index: number;
  children?: React.ReactNode;
}

 type MyTabPanelState = {}

class MyTabPanel extends React.Component<MyTabPanelProperties, MyTabPanelState> {

  render(): JSX.Element {
    const { children, value, index } = this.props;
    const element = (
      <div role="tabpanel" hidden={value !== index} aria-labelledby={'simple-tab'}>
        {children}
      </div>
    );

    return element;
  }
}

interface MyTabsProperties<T = MyTabPanelProperties> {
  value?: number;
  children: React.ReactElement<T> | Array<React.ReactElement<T>> | React.Component<T> | Array<React.Component<T>>;
}

interface MyTabsState {
  value: number;
}

class MyTabs extends React.Component<MyTabsProperties, MyTabsState> {
  constructor(props: MyTabsProperties) {
    super(props);
    this.state = {
      value: this.props.value ? this.props.value : 0
    };
  }

  private checkChildrenName(children: ReactNode, names: string | Array<string>): boolean {
    const childs = React.Children.toArray(children);
    let check = false,
        allNames = new Array<string>();

    if (names instanceof Array) {
      allNames = names;
    } else {
      allNames = names.split(' ');
    }

    if (allNames.length === 0) {
      throw Error('Must specify names for check children');
    }

    check = childs.every((child) => {
      let check = false;
      const name = this.getComponentChildName(child);
      if (name) {
        check = allNames.includes(name);
      }
      return check;
    });

    return check;
  }

  private getComponentChildName(component: any): string | undefined {
    const type = (component.type || {});
    let name = undefined;

    if (component) {
      name = component.displayName ||
      component.name ||
      type.displayName ||
      type.name ||
      undefined;
    }

    return name;
  }

  render(): JSX.Element {
    const { children } = this.props;
    let element: JSX.Element;
    if (this.checkChildrenName(children, 'MyTabPanel')) {
      element = (
        <div>
          <AppBar position='static'>
            <Tabs value='0' onChange={this.handleChange.bind(this)}>
              {React.Children.map(children, (child, index) => {
                return this.renderAppBarTab(child, index);
              })}
            </Tabs>
          </AppBar>
          {children}
        </div>
      );
    } else {
      throw Error('Must use MyTabPanel component in a MyTabs component');
    }
    return element;
  }

  private handleChange(event: React.ChangeEvent<{}>, newValue: number): void {}

  private renderAppBarTab(child, index: number): JSX.Element {
    const label = 'Test';
    const element = (
      <Tab label={label} key={index} />
    );

    return element;
  }

  set value(value:number) {
    this.setState({value: value});
  }
}


ReactDOM.render(
  <React.StrictMode>
    <MyTabs>
      <MyTabPanel label='Tab1'>
        <Typography>Test1</Typography>
      </MyTabPanel>
      <MyTabPanel label='Tab2'>
        <Typography>Test2</Typography>
      </MyTabPanel>
    </MyTabs>
  </React.StrictMode>
  , document.getElementById('root'));

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

Мой родительский компонент MyTabset мой дочерний компонент MyTabPanel .

Мне понадобится ваша помощь по следующим пунктам:

  • В моем родительском компоненте, renderAppBarTab метод, я хочу получить свойства моего дочернего компонента, в частности, свойство метки (сегодня я установил жесткую константу, потому что не знаю, что делать)? TypeScript не позволяет мне вызывать props для моих детей, потому что тип моего дочернего параметра ему не подходит. Я хотел бы получить свойства типа, используемого для моего дочернего компонента (MyTabPanelProperties).
  • Я хотел бы обновить скрытое свойство моего дочернего компонента, изменив статус моего родительского компонента (свойство значения) , как в примере, предложенном material-ui.

У кого-нибудь есть идеи?

Заранее спасибо

1 Ответ

0 голосов
/ 03 июня 2020

Я продолжал искать решение и кое-что нашел: https://codepen.io/Answerrer/pen/ExPxmEN.

interface MyTabPanelProperties {
  label: string;
  children?: React.ReactNode;
}

 type MyTabPanelState = {}

class MyTabPanel extends React.Component<MyTabPanelProperties, MyTabPanelState> {

  render(): JSX.Element {
    const { children, value, index } = this.props;
    const element = (
      <div role="tabpanel" aria-labelledby={'simple-tab'}>
        {children}
      </div>
    );

    return element;
  }
}

interface MyTabsProperties<T = MyTabPanelProperties> {
  selectedTab?: unknown;
  children: React.ReactElement<T> | Array<React.ReactElement<T>> | React.Component<T> | Array<React.Component<T>>;
}

interface MyTabsState {
  selectedTab?: unknown;
}

class MyTabs extends React.Component<MyTabsProperties, MyTabsState> {
  constructor(props: MyTabsProperties) {
    super(props);

    const { children } = this.props;
    const allChilds = React.Children.toArray(children);
    let selectedChildId;

    if (allChilds.length > 0) {
      selectedChildId = (allChilds[0] as MyTabPanel).props.id;
    } else {
      selectedChildId = 0;
    }

    this.state = {
      selectedTab: this.props.selectedTab ? this.props.selectedTab : selectedChildId
    };
  }

  private checkChildrenName(children: ReactNode, names: string | Array<string>): boolean {
    const childs = React.Children.toArray(children);
    let check = false,
        allNames = new Array<string>();

    if (names instanceof Array) {
      allNames = names;
    } else {
      allNames = names.split(' ');
    }

    if (allNames.length === 0) {
      throw Error('Must specify names for check children');
    }

    check = childs.every((child) => {
      let check = false;
      const name = this.getComponentChildName(child);
      if (name) {
        check = allNames.includes(name);
      }
      return check;
    });

    return check;
  }

  private getComponentChildName(component: any): string | undefined {
    const type = (component.type || {});
    let name = undefined;

    if (component) {
      name = component.displayName ||
      component.name ||
      type.displayName ||
      type.name ||
      undefined;
    }

    return name;
  }

  render(): JSX.Element {
    const { children } = this.props;
    let element: JSX.Element;
    if (this.checkChildrenName(children, 'MyTabPanel')) {
      element = (
        <div>
          <AppBar position='static'>
            <Tabs value='0' onChange={this.handleChange.bind(this)}>
              {React.Children.map(children, (child, index) => {
                return this.renderAppBarTab((child as MyTabPanel), index);
              })}
            </Tabs>
          </AppBar>
          {React.Children.map(children, (child, index) => {
            const id = (child as MyTabPanel).props.id;
            if (selectedTab === id) {
              return child;
            }
          })}
        </div>
      );
    } else {
      throw Error('Must use MyTabPanel component in a MyTabs component');
    }
    return element;
  }

  private handleChange(event: React.ChangeEvent<{}>, selectedTab: unknown): void {
    this.selectedTab = selectedTab;
  }

  set selectedTab(selectedTab: unknown) {
    this.setState({ selectedTab: selectedTab });
  }

  private renderAppBarTab(child:MyTabPanel, index: number): JSX.Element {
    const { label, id } = child.props;
    const element = (
      <Tab label={label} value={id} />
    );

    return element;
  }
}


ReactDOM.render(
  <React.StrictMode>
    <MyTabs>
      <MyTabPanel label='Tab1'>
        <Typography>Test1</Typography>
      </MyTabPanel>
      <MyTabPanel label='Tab2'>
        <Typography>Test2</Typography>
      </MyTabPanel>
    </MyTabs>
  </React.StrictMode>
  , document.getElementById('root'));

Вместо изменения статуса дочерних элементов в родительском рендере я отображал только дочерний для отображения (тот, идентификатор которого соответствует выбранной вкладке). На данный момент это работает. Однако есть кое-что, что я сделал, что мне не очень нравится, потому что я не понимаю и не знаю, имею ли я право это делать (в любом случае, никаких ошибок компиляции и выполнения моего проекта). Пришлось бросить своих детей. Не знаю, имею ли я на это право. Может ли кто-нибудь меня прояснить? Имею ли я право бросить своего ребенка, как в моем случае? Спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...