我正在开发一个UI库,我的一些组件改变了他们的状态"基于用户的一些互动。
例如,用户点击手风琴面板的标题会导致手风琴面板打开并变为可见。通过将visible
修饰符添加到accordion面板来实现此状态,如下所示:
<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
的好处。
谢谢!
答案 0 :(得分:1)
据我了解您的疑虑,您只想将后端/通信和业务逻辑放入&#34; React状态&#34;。但正如你自己指出的那样,还有与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为&#34;简单&#34;应避免国家修改。通常这只是必需的,如果你包括第三方库(jQuery之类)或WebComponents/Custom Elements,因为他们可能不会很好地使用React道具等开箱即用。
关于评论中的问题:执行渲染FrequentlyAskedQuestions(FAQs)的任务,组件可能如下所示:
与应用程序相关的UI组件(&#34;愚蠢&#34;),。/ component /FAQ.js:
const FAQ = ({faqs}) => (
<Accordion>
{faqs.map(faq => (
<AccordionPane title={faq.title} content={faq.text} />
))}
</Accordion>
)
并且 - 使用redux - 相应的容器,./containers /FAQ.js:
import {connect} from 'react-redux'
import FAQ from './FAQ'
const mapStateToProps = state => state.faqs
export default connect(mapStateToProps)(FAQ)