我可以在 React 组件中解耦布尔状态吗

Posted

技术标签:

【中文标题】我可以在 React 组件中解耦布尔状态吗【英文标题】:Can I decouple boolean state in React component 【发布时间】:2021-04-28 10:17:04 【问题描述】:

如下简单的反应组件代码:

class Test extends React.Component 
    constructor(props)
        super(props)
        this.c1 = this.c1.bind(this);
        this.c2 = this.c2.bind(this);
        this.state = 
            a:false,
            b:false
        
    

    c1(e) 
        this.setState(a:true, b:false)
    

    c2(e) 
        this.setState(a:false, b:true)
    


    render() 
        return (
            <div>
                <div>
                    <input name="n" type="radio"  onChange=this.c1 />
                    <input name="n" type="radio"  onChange=this.c2 />
                </div>
                <div>
                    
                        this.state.a && "aa"
                    
                    
                        this.state.b && "bb"
                    
                </div>
            </div>
        )
    

代码只是在单击单选按钮时切换显示“aa”或“bb”。但是,如果我添加一个显示“cc”的新单选按钮来实现相同的功能。我应该:

    添加新状态,如“c” 添加新的输入 html 并实现其回调 此回调中的 setState 'c'

所有这些都可以,但是我必须更改 'c1','c2' 函数,使我的代码耦合如下:

class Test extends React.Component 
    constructor(props)
        super(props)
        this.c1 = this.c1.bind(this);
        this.c2 = this.c2.bind(this);
        this.c3 = this.c3.bind(this);
        this.state = 
            a:false,
            b:false,
            c:false,
        
    

    c1(e) 
        this.setState(a:true, b:false, c:false) // add 'c:false' which coupled
    

    c2(e) 
        this.setState(a:false, b:true, c:false) // add 'c:false' which coupled
    

    c3(e) 
        this.setState(a:false, b:false, c:true)
    


    render() 
        return (
            <div>
                <div>
                    <input name="n" type="radio"  onChange=this.c1 />
                    <input name="n" type="radio"  onChange=this.c2 />
                    <input name="n" type="radio"  onChange=this.c3 />
                </div>
                <div>
                    
                        this.state.a && "aa"
                    
                    
                        this.state.b && "bb"
                    
                    
                        this.state.c && "cc"
                    
                </div>
            </div>
        )
    

我认为这种情况在 React 中很常见。所以无论我添加多少单选按钮,我都想解耦我的代码。我不需要更改代码,只需添加代码即可满足“开闭原则”。

你有什么建议吗?提前致谢。

【问题讨论】:

看起来 *** question 可能是您要找的 (?) 感谢@DorWeid,这是关于设计模式,而不是关于如何使用单选按钮。 【参考方案1】:

我认为你可以这样做

class Test extends React.Component 
    constructor(props)
        super(props)
        this.change = this.change.bind(this);
        this.state = 
            a:false,
            b:false,
            c:false,
        
    

   change = statename => e => 
    this.setdefault();
    this.setState(
        [statename]: true
      );
  ;
  setdefault()
    this.setState(
      a:false,
      b:false,
      c:false,
    );
  


    render() 
        return (
            <div>
                <div>
                    <input name="n" type="radio"  onChange=this.change("a") />
                    <input name="n" type="radio"  onChange=this.change("b") />
                    <input name="n" type="radio"  onChange=this.change("c") />
                </div>
                <div>
                    
                        this.state.a && "aa"
                    
                    
                        this.state.b && "bb"
                    
                    
                        this.state.c && "cc"
                    
                </div>
            </div>
        )
    

【讨论】:

【参考方案2】:

鉴于属性是相互依赖的而不是独立的,我觉得将状态存储为键控 boolean 值的方式不合适。

现在,state 有四个选项:

 a:false, b:false, c:false  // initial state from constructor
 a:true, b:false, c:false  // from c1
 a:false, b:true, c:false  // from c2
 a:false, b:false, c:true  // from c3

根据您的 setState 回调,一次不能超过 1 个属性为 true

如果每个属性都是独立的,那么您将有 8 个选项 (2^3),并且这种设置是有意义的。

事实上,我建议您只存储哪个值是true。您可以通过查看单个选项是否与存储的 selected 值匹配来检查单个选项是否为 true

无论有多少按钮,您都希望Component 工作,所以让我们将按钮选项传递为props。就设计模式而言,这就是“依赖注入”。我们可以创建一个SelectOne,它不需要知道它的选项是什么。在这里,我只是希望 optionsstring,例如 "aa""bb",但您可以将其重构为具有属性 labelvalueobject

我们要循环遍历optionsarray 并渲染每一个。有时您会看到它被提取到组件的方法中,例如 this.renderOption(i),但您也可以在 render() 中进行内联映射。

您实际上并未在回调中使用事件e。您可以使用该事件来获取e.target.value,假设您在输入中设置了 value 属性(有关 HTML 标记,请参阅MDN docs)。您还可以通过为onChange 创建一个匿名箭头函数将值传递给回调,该函数使用正确的值调用它。我正在这样做。

这里它作为一个类组件,因为这是你以前使用的。

class SelectOne extends React.Component 

  constructor(props) 
    super(props);
    this.state = 
      selected: undefined // this.state.selected will initially be undefined
    
  

  render() 
    return (
      <div>
        <div>
          this.props.options.map((optionName) => (
            <label htmlFor=optionName>
              optionName
              <input
                type="radio"
                id=optionName
                name="n"
                value=optionName
                checked=this.state.selected === optionName
                onChange=() => this.setState(selected: optionName)
              />
            </label>
          ))
        </div>
        this.state.selected !== undefined && (
          <h2>Selected: this.state.selected</h2>
        )
      </div>
    );
  

这里是作为一个函数组件。这是较新的语法,所以如果您只是在学习,那么我建议您使用函数组件和钩子来学习。销毁 props 可以很容易地接受可选的 props 并设置它们的默认值。我允许在此处传递inputname 属性,但如果未提供,则默认为您的"n"

const SelectOne = (options, name = "n") => 
  // will be either a string or undefined
  const [selected, setSelected] = React.useState(undefined);

  return (
    <div>
      <div>
        options.map((optionName) => (
          <label htmlFor=optionName>
            optionName
            <input
              type="radio"
              id=optionName
              name=name // from props
              value=optionName
              checked=selected === optionName
              onChange=() => setSelected(optionName)
            />
          </label>
        ))
      </div>
      selected !== undefined && (
          <h2>Selected: selected</h2>
      )
    </div>
  );

无论哪种方式,您都可以这样称呼它:

<SelectOne options=["aa", "bb", "cc"] />

您还可以为给定的options 集合创建一个特定的组件,您现在可以在没有道具的情况下调用它。

const SelectABC = () => <SelectOne options=["aa", "bb", "cc"] />;
<SelectABC/>

【讨论】:

以上是关于我可以在 React 组件中解耦布尔状态吗的主要内容,如果未能解决你的问题,请参考以下文章

关于vue-router传参的理解

Vue Router:使用 props 将组件和路由解耦

关于redux

react-dnd

react-dnd 拖拽

解决解耦组件中的冲突功能