将boolean state传递给多个子节点

时间:2018-04-20 05:04:15

标签: javascript reactjs ecmascript-6

预期 我想在选项卡的内容中加载一个加载状态,每当用户点击选项卡标题切换选项卡时,标记就会传递给子项。

问题 我有这个App组件,我使用setTimeout伪装它的api调用

class App extends Component {

  state = {
    loading: false,
    data: []
  }

  getData = () => {
      return new Promise(resolve => {
        return setTimeout(() => {
          resolve(
            [
              {
                id: 1,
                name: "Kelas A",
                list: ["Jane", "Ali", "Ahmad"]
              },
              {
                id: 2,
                name: "Kelas B",
                list: ["May", "Henry", "Ben"]
              }
            ]
          )
        },500)
      })
  }

  async componentDidMount() {
    this.setState({
      loading: true
    })

    const data = await this.getData()
    this.setState({
      data,
      loading: false
    })
  }

  //loadingComponent = () => <div>Loading...</div>;

  render() {

    const { data, loading } = this.state

    return (
      <Tabs 
        activeTab={1} 
        loading={loading} 
        //loadingComponent={this.loadingComponent()}
      >
        {data.map(o => (
          <Tab 
            id={o.id}            
          >
            <Tab.Title>{o.name}</Tab.Title>
            <Tab.Content>
              {o.list.join(", ")}
            </Tab.Content>
          </Tab>
        ))}
      </Tabs>
    );
  }
}

我将加载状态作为prop传递给Tabs子组件,它工作,我可以看到真和假:

class Tabs extends Component {
  static defaultProps = {
    activeTab: 1
  };

  static getDerivedStateFromProps(nextProps, prevState) {
    if(nextProps.loading !== prevState.loading){
      return {
        loading: nextProps.loading
      }
    }
  }

  state = {
    activeTab: this.props.activeTab
  };

  changeTab = tab => {
    this.setState({ activeTab: tab });
  };

  render() {
    const { children } = this.props;
    const { activeTab, loading } = this.state;

    console.log('true or false before pass to children', loading)

    return (
      <div className="tabs">
        {React.Children.map(children, child =>
          React.cloneElement(child, {
            loading,
            activeTab,
            changeTab: this.changeTab
          })
        )}
      </div>
    );
  }
}

但是我把那个装载作为支柱传递给Tabs的孩子,这是Tab,加载标志变得虚假?我无法发现问题。

class Tab extends Component {
  static Title = ({ children, tabId, activeTab, handleTabClick }) => {
    return (
      <div
        className={`title ${tabId === activeTab ? "active" : ""}`}
        onClick={handleTabClick}
      >
        {children}
      </div>
    );
  };

  static Content = ({ children, tabId, activeTab, loading }) => {

    loading && 'Loading...' //won't work coz loading is always false, I wonder why

    return tabId === activeTab ? (
      <div className="content">{children}</div>
    ) : null;
  };

  render() {
    return React.Children.map(this.props.children, child =>
      React.cloneElement(child, {
        handleTabClick: () => this.props.changeTab(this.props.id),
        tabId: this.props.id,
        activeTab: this.props.activeTab,
        loading: this.props.loading // why always false?
      })
    );
  }
}

我的演示 https://codesandbox.io/s/o41r35n2qz

2 个答案:

答案 0 :(得分:0)

您的子组件中

this.props.loading始终为false,因为它true时甚至无法呈现,dataloadingtrue为空{1}}因此data.map不会创建任何组件。

即使data为空,您也需要将加载检查移动到呈现的父组件。例如。 https://codesandbox.io/s/xpy3r3575z

编辑: 如果要为每个选项卡单独获取数据,则需要创建单独的API调用,一个用于获取选项卡标题以呈现选项卡标题,另一个用于获取活动选项卡的数据。

答案 1 :(得分:0)

最初将其设置为false

state = {
    loading: false,
    data: []
  }

加载组件后,将其模拟为true,然后设置为false

async componentDidMount() {
    this.setState({
      loading: true 
    })

    const data = await this.getData()
    this.setState({
      data,
      loading: false
    })
  }

但您从未收到子组件中的组件状态更改。

您可以使用componentWillReceiveProps()

更改状态

所以你可以尝试一下

class Tab extends Component {

 componentWillReceiveProps(props) { //here you will get changes whenever state changes
   let loading  = props.loading
   this.setState({loading});
  }

  render() {
    return React.Children.map(this.props.children, child =>
      React.cloneElement(child, {
        handleTabClick: () => this.props.changeTab(this.props.id),
        tabId: this.props.id,
        activeTab: this.props.activeTab,
        loading: this.state.loading //through state
      })
    );
  }
}