首页 > 解决方案 > ReactJS 和 Typescript - 子组件:在父状态更改时获取子道具并刷新子组件

问题描述

我需要关于 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

在以下几点上,我需要您的帮助:

有人有想法么?

提前致谢

标签: reactjstypescriptvalidation

解决方案


我一直在寻找解决方案并找到了一些东西: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'));

在父级的渲染中,我没有更改子级的状态,而是仅显示要显示的子级(其标识符对应于所选选项卡的那个)。暂时有效。但是,我做了一些我不太喜欢的事情,因为我不明白,我不知道我是否有权这样做(无论如何,我的项目没有编译错误和执行)。我不得不让我的孩子们投产。我不知道我是否有权这样做。有人可以清理我吗?我是否有权像在我的情况下那样为我的孩子投胎?谢谢


推荐阅读