【问题标题】:React Stateless Components: Interacting with their output and appearanceReact Stateless Components:与它们的输出和外观交互
【发布时间】:2020-04-29 19:13:21
【问题描述】:

我已经四处寻找答案——我发现最接近的是this question——但我认为我的情况有很大的不同(事实上它开始进入持有其孩子状态的父母...孩子们)这最终导致我要求澄清。

我的意思的一个非常简单的例子如下(希望能更好地说明我的要求):

假设我们有一堆类似的书籍文档

bookList = [
{
  title: "book 1", 
  author: "bob", 
  isbn: 1, 
  chapters: [
{ chapterNum: 1, chapterTitle: "intro", chapterDesc: "very first chapter", startPg: 2, endPg: 23 }, 
{ chapterNum: 2, chapterTitle: "getting started", chapterDesc: "the basics", startPg: 24, endPg: 45 }
]},
{
 title: "book 2" ... }
]

所以重点是文档中的这些嵌入对象可能很长,因此可能会折叠/展开。

然后这里是显示组件的粗略代码示例

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            books: bookList,
            focusBook: null
        }
        this.updateDetailDiv = this.updateDetailDiv.bind(this);
    }

    updateDetailDiv(book) {
        this.setState(
            { focusBook: book}
        );
    }

    render() {
        return(
            <BookList 
                bookList = {this.state.books} 
                updateDetailDiv = { this.updateDetailDiv }
            />
            <BookDetail
                focusBook = { this.state.focusBook }
            />
        );
    }
}

const BookList = props => {
    return (
        props.bookList.map(item=>
            <li onClick={()=> props.updateDetailDiv(item)}> {item.title} </li>
            )
        );
}

const BookDetail = props => {
     return (
        <div className="bookDetails">
        { props.focusBook != null
        ? <div>
            {props.focusBook.title}, 
            {props.focusBook.author},
            {props.focusBook.isbn}
            Chapters:
            <div className="chapterList">
                { props.focusBook.chapters.map(item=>
                    <span onClick={()=>someFunction(item)}>{item.chapterNum} - {item.chapterName}</span>
                    )}
            </div>
            <div id="chapterDetails">
                This text will be replaced with the last clicked chapter's expanded details
            </div>
        </div>
        : <div>
            Select A Book
         </div> 
     })
}

someFunction(item) {
 document.getElementById('chapterDetails').innerHTML = `<p>${item.chapterDesc}</p><p>${item.startPg}</p><p>${item.endPg}</p>`;
}

所以我的问题是我不确定在不将其传递给父组件的情况下处理功能性无状态组件中数据的简单外观/视觉更改的最佳方法是什么——这对第一个孩子来说很好并且有意义- 但是当许多孩子都有自己的孩子(他们可能有自己的孩子)时会发生什么--> 都需要自己的渲染选项? 例如 - 此处 App 组件将重新渲染 DetailDiv 组件(因为状态已更改) - 但我不希望 App 也处理 DetailDiv 的详细 div。在我的示例中,这一切都非常简单,但我正在处理的实际应用程序具有 2 或 3 层嵌入式项目——一旦由 App 呈现——实际上可以通过普通 JS 直观地修改。

所以在我的示例中,您会看到我在每个章节列表中都有一个 someFunction() - 我可以通过编写一个单独的简单“传统 JS DOM 函数”来完成这项工作(即:target.getElementByIdclosest() --但我不认为我应该在使用 React 时使用普通的 JS 来操作 DOM。

再次总结一下——对无状态组件的渲染输出进行简单的 DOM 操作的最佳方式是什么?将这些变成自己的类似乎有点过头了——让“父”应用程序处理它的“孙子”和“曾孙”状态将随着应用程序的增长而变得笨拙。我一定错过了一个明显的例子,因为在没有状态组件层的情况下,我没有看到太多处理这个问题的方法。

编辑为清楚起见:

BookDetail 是一个无状态组件。

父有状态组件将对象作为道具传递给它(App

App的状态发生变化时,它会再次渲染,反映变化。

假设BookDetail负责显示大量数据。

我想要它,这样BookDetail 中的每个span 在单击时都会在chapterDetail div 中显示其相关的item。 如果单击另一个span,则chapterDetail div 将填充那个 item 的详细信息。 (这只是一个简单的例子——它可以是对某个无状态组件的任何其他纯外观更改——在这种情况下,父母必须跟踪它似乎有点过分了)

我不知道如何在渲染后更改无状态组件的 UI/外观而不给它状态或让父级跟踪本质上是“子状态”的内容(因为这是更新外观的唯一方法组件的状态是改变其状态,触发渲染)。

有没有办法在不使 BookDetail 成为有状态组件的情况下做到这一点?

【问题讨论】:

  • 您的“问题”很难理解。 Presentation/UI 实际上与具有或不具有内部状态的组件无关。是的,在 React 中操作 DOM 是一种反模式。你能更清楚(即明确)你想要做什么吗?
  • 您好,我试图清除它。
  • 好的,听起来你有一个映射“章节”组件数组的“书籍详细信息”组件,当单击“章节”时,您想要它打开章节详细信息。假设您在任何给定时间只想要一个“章节”“打开/扩展”是否安全? 根据您的原始措辞,您有一个章节列表,单击特定章节后,章节的详细信息会显示在“chapterDetails”div 中?其中一个听起来正确吗?
  • 嘿,是的,两者的结合非常接近 - 一个 BookDetail 组件,它映射一个“章节”、“按钮”(或跨度,或任何可点击的)数组,当一个“章节”是点击后,那个章节的详细信息会显示在 chapterDetails div 中。 (在任何给定时间只有一个)

标签: reactjs jsx


【解决方案1】:

您可以在功能组件中添加一点简单的状态来跟踪选定的索引。在这种情况下,我将在 state 中存储一个“选定的章节索引”,然后在 div 中呈现“chapters[index].details”,所有这些都无需操作作为 React 反模式的 DOM。

这里的用例是所选章节是 BookDetail 关心的内部细节,所以不要将此“状态”提升为父组件,因为它也只在 BookDetail 的生命周期内相关,所以没有必要将这个选定的索引存储在应用程序范围的状态管理系统中,比如 redux。

const BookDetail = ({ focusBook }) => {
  // use a state hook to store a selected chapter index
  const [selectedChapter, setSelectedChapter] = useState();

  useEffect(() => setSelectedChapter(-1), [focusBook]);

  if (!focusBook) {
    return <div>Select A Book</div>;
  }

  const { author, chapters, isbn, title } = focusBook;

  return (
    <div className="bookDetails">
      <div>
        <div>Title: {title},</div>
        <div>Author: {author},</div>
        <div>ISBN: {isbn}</div>
        Chapters:
        <div className="chapterList">
          {chapters.map(({chapterName, chapterNum}, index) => (
            <button
              key={chapterName}
              onClick={() => setSelectedChapter(selectedChapter >= 0 ? -1 : index)} // set the selected index
            >
              {chapterNum} - {chapterName}
            </button>
          ))}
        </div>

        // if a valid index is selected then render details div with 
        // chapter details by index
        {chapters[selectedChapter] && (
          <div id="chapterDetails">
            {chapters[selectedChapter].details}
          </div>
        )}
      </div>
    </div>
  );
};

演示

【讨论】:

  • 用例描述完美。这很好用,除了当我更改 App 的 focusBook 状态并且 BookDetail 再次呈现时 - chapterDetails div 仍然显示上次单击章节的详细信息。我仍在努力 - 因为我可能只是设置了错误 - 但例如 selectedChapter 在 BookDetail 渲染之间是持久的。 (虽然点击时它会改变 - 当 BookDetail 再次呈现(通过 App setState)时,它似乎没有“重置”
  • 啊,我想我知道为什么了。我想也许你的BookDetails 没有接受道具更改和重新渲染。我更新了我的沙箱以拥有两本书,但正如预期的那样,在选择新书时它似乎会重新呈现。如果可能,您可以将代码上传到正在运行的沙箱并在您的问题中分享吗?
  • 我不确定你是否在之后添加了它,或者我完全错过了它,但似乎解决它的是 CodeSandbox 中的 React.useEffect() 行。一旦我添加它开始工作。谢谢你一吨:D!您知道再次单击章节(按钮)会隐藏它的方法吗? (即:如果已经选择了这一章 - 隐藏它)。尝试过类似onClick()=&gt;{ selectedChapter == index ? setSelectedChapter(-1) : setSelectedChapter(index) }
  • 是的。对onClick 处理程序的简单修改以“取消设置”有效索引,例如onClick={() =&gt; setSelectedChapter(selectedChapter &gt;= 0 ? -1: index)}。这里的条件是如果它是一个大于或等于0的有效索引,则将其设置回-1,否则它当前不是有效索引,因此设置它。我更新了沙盒。
  • 完美运行。再次感谢您。
【解决方案2】:

您可以采取一些方法来解决此问题。 首先,你不需要为你的函数组件创建一些类组件,相反,你可以使用 react hooks,比如useState,这样组件就可以控制它自己的内容。

现在,如果您不想使用 React Hooks,您可以使用 React Redux store 来管理您的所有状态:您只能使用 Redux 操作更改状态值。

编码愉快! :D

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    相关资源
    最近更新 更多