首页 > 解决方案 > 在 React 组件之间共享状态

问题描述

我有一个表格组件(父级),并且在表格的每一行中,还有另一个组件基本上是一个图像按钮(子级)。单击时,图像按钮会从其默认的三个垂直点(https://png.icons8.com/windows/1600/menu-2.png) 到下载图标。三个垂直点图像有一个 onClick 监听器,将其切换到下载图标,下载图标有一个 onClick 监听器,用于下载表格行中的文件。每个图像按钮都有自己的状态(基于要显示的图像)。在表格组件中,我有一个包裹整个屏幕的 div。单击该 div 时(基本上,如果您单击图像之外的任何位置),我希望能够将表中的所有图像重置回三个点。我不确定如何做到这一点,因为有很多子组件,每个子组件都有自己的状态(因此我认为 redux 不会工作)。有没有办法与表格组件共享每个图像按钮的状态?

这是一些表格代码

<div className='shield' onClick={() => this.resetDownloads()}></div>
<table className='table table-striped'>
  <tbody>
    this.props.songs.data.map((song, index) => 
      <tr key={index}>
       <td>{song.name}</td>
       <td>{song.artist}</td>
       <td>{song.duration}</td>
       <td><ActionButton song={song}/></td>
      </tr>
  </tbody>
 </table>

这是一些 ActionButton 组件

class ActionButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            download: false
        };
        this.toggle = this.toggle.bind(this);
    }

    toggle(e) {
        e.stopPropagation();
        this.setState({
            download: !this.state.download
        });
    }

    render() {
      return this.state.download ? (
        <div>
          <img src={Download} width='15%' onClick={(e) => this.props.downloadSong(this.props.song, e)}></img>
        </div>
      )
      : (
        <div>
          <img src={Dots} width='15%' onClick={(e) => this.toggle(e)} className='image'></img>
        </div>
      )
    }
}

标签: javascriptreactjs

解决方案


这是你提升你的状态的地方ActionButton实际上,首先,您的组件中不需要状态。它应该是一个无状态组件。您可以将所有数据保存在父组件中。

假设id歌曲数据中有一个属性。您可以在父组件中跟踪 adownloadState并将这首歌添加id到此状态对象。然后您可以将此值传递给您ActionComponent并使用它。此外,您可以将所有功能保留在父组件中。

const songs = [
  { id: "1", name: "Always Blue", artist: "Chet Baker", duration: "07:33" },
  { id: "2", name: "Feeling Good", artist: "Nina Simone", duration: "02:58" },
  { id: "3", name: "So What", artist: "Miles Davis", duration: "09:23" },
]

class App extends React.Component {
  state = {
    downloadState: {},
  }
  
  toggle = ( e, id ) => {
    e.stopPropagation();
    this.setState( prevState => ({
      downloadState: { ...prevState.downloadState, [id]: !prevState.downloadState[id]}
    }))
  }

  downloadSong = ( e, song ) => {
    e.stopPropagation();
    alert( song.name );
  }

  resetDownloads = () => this.setState({ downloadState: {}});
  

  render() {
    return (
      <div onClick={this.resetDownloads}>
      <table>
        <tbody>
        {
          songs.map((song, index) => (
          <tr key={index}>
            <td>{song.name}</td>
            <td>{song.artist}</td>
            <td>{song.duration}</td>
            <td>
            <ActionButton
              toggle={this.toggle}
              song={song}
              downloadState={this.state.downloadState}
              downloadSong={this.downloadSong}
            />
            </td>
          </tr>
          ))
        }
        </tbody>
      </table>
      </div>
    )
  }
}

const ActionButton = props => {
  const { downloadState, downloadSong, song, toggle } = props;
  
  const handleToggle = e => toggle(e, song.id);
  const handleDownload = e => downloadSong( e, song );
  
  const renderImages = () => {
    let images = "";
    if ( downloadState[song.id] ) {
      images = <p onClick={handleDownload}>Download</p>;
    } else {
      images = <p onClick={handleToggle}>Dots</p>;
    }
    return images;
  }

  return (
    <div>{renderImages()}</div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
table, th, td {
    border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

如果没有任何id属性,那么您可以使用索引设置相同的逻辑,但我认为每个数据都应该有一个id:) 也许可以将相同的逻辑用于歌曲名称而不是使用索引,因为它们几乎是唯一的。谁知道 :)


推荐阅读