2

我正在开发一个 UI 库,我的一些组件会根据用户的一些交互来改变它们的“状态”。

例如,用户单击手风琴面板的标题会导致手风琴面板打开并变得可见。这种状态是通过将visible修饰符添加到手风琴面板来实现的,如下所示:

<div class="accordion">
    <div class="accordion_panel-visible">
        <div class="accordion_title">foo</div>
        <div class="accordion_content">bar</div>
    </div>
    <div class="accordion_panel">
        <div class="accordion_title">fizz</div>
        <div class="accordion_content">buzz</div>
    </div>
</div>

我之前的假设是 React State 应该用于根据一些后端数据重新渲染组件。但是,基于查看其他 UI 库等的源代码,它们似乎也在使用 React State 处理 UI 状态。

所以使用 React,我可以通过使用原始 DOM API 来实现我想要的(如这里的示例所示 - https://reactjs.org/docs/refs-and-the-dom.html):

Accordion.defaultProps = {
    name: 'accordion'
};

class Accordion extends React.Component {
    toggle(event) {
        const panel = event.target.closest('[data-component="panel"]');
        const operator = panel.modifier('active') ? 'unset' : 'set';

        panel.modifier('active', operator); 
    }

    render() {
        return (
            <Module {...this.props}>
                {this.props.panels.map(({ title, content }, index) => (
                    <Component name='panel' key={index}>
                        <Component name='title' onClick={this.toggle}>{title}</Component>
                        <Component name='content'>{content}</Component>
                    </Component>
                ))}
            </Module>
        )
    }
}

这一切都很好 - 但我本质上是从 React 组件中进行 DOM 操作,我经常阅读应该避免。在这种情况下,我应该使用 React State 和 Refs(而不是直接的 DOM 操作)。为了实现与上述相同的目标,我相信我可以做到:

Accordion.defaultProps = {
    name: 'accordion'
};

class Accordion extends React.Component {
    constructor(props) {
        super(props);

        this.panels = [];
        this.state = { activePanel: null };
    }

    toggle(index) {
        this.setState({ 
            activePanel: (this.panels[index] === this.state.activePanel) ? null : this.panels[index]
        });
    }

    isActive(index) {
        return (this.panels[index] === this.state.activePanel) ? true : false;
    }

    render() {
        return (
            <Module {...this.props}>
                {this.props.panels.map(({ title, content }, index) => (
                    <Component name='panel' 
                        key={index} 
                        ref={ref => this.panels[index] = ref}
                        modifiers={this.isActive(index) ? 'active' : false}
                    >
                        <Component name='title' onClick={this.toggle.bind(this, index)}>
                            {title}
                        </Component>
                        <Component name='content'>{content}</Component>
                    </Component>
                ))}
            </Module>
        )
    }
}

(我知道上述代码段的行为会关闭兄弟面板,第一个代码段不是这种情况,但这是微不足道的,可以忽略)。

所以我的问题是,我应该为此使用 React State(即后一个示例)吗?

感觉如果我的应用程序基于不需要/修改/发布/更新/获取/接收数据的用户交互显示/隐藏/打开/关闭 UI 元素,那么 React 实际上不应该关心它们。

但最重要的是——这取决于偏好吗?是否应该由来决定 React 是否应该关心这个?归根结底,React 是一个工具,我目前正在使用该工具在后端免费环境中创建和渲染 UI 组件。我现在真的很困惑,不确定我是否真的能看到state在这种情况下使用的好处。

谢谢!

4

1 回答 1

1

据我了解您的担忧,您只想将后端/通信和业务逻辑置于“反应状态”。但正如您自己指出的那样,还有与 UI 元素相关的状态。手风琴关闭/打开等。

可以将其分为两类:

   - UI State (only for representation purposes)
   - Business Logic state

你可以同时使用 React 的组件状态。

在构建或使用 UI 组件库时,为了封装,状态通常在这些库中保存/管理。对于复杂的应用程序,我建议使用一些更强大的状态管理,比如react-redux。但也有一些情况是人们将 react-redux 用于 UI业务逻辑状态。

从可重用性的角度来看,我建议使用不了解实际应用程序用例的 UI 组件(如 Accordion、FancyButton、Snackbar 等)。以及组成整个页面或部分页面的另一个组件集合(页眉、页脚、导航、MainView 等)。

然后 - 当使用 redux 时 - 你可以拥有所谓的容器,它将组件与 redux 的存储/状态管理连接起来。

在任何情况下,都应该避免使用 refs 进行“简单”的状态修改。通常只有在包含 3rd 方库(类似 jQuery)或WebComponents/Custom Elements时才需要这样做,因为它们可能无法很好地与 React 道具等配合使用。

关于评论中的问题:具有呈现常见问题(FAQ)的任务,组件可能如下所示:

应用相关的 UI 组件(“愚蠢”),./components/FAQ.js:

const FAQ = ({faqs}) => (
  <Accordion>
    {faqs.map(faq => (
      <AccordionPane title={faq.title} content={faq.text} />
    ))}
  </Accordion>
)

并且 - 使用 redux - 相应的 Container,./containers/FAQ.js:

import {connect} from 'react-redux'
import FAQ from './FAQ'

const mapStateToProps = state => state.faqs

export default connect(mapStateToProps)(FAQ)
于 2018-06-04T07:38:13.200 回答