【问题标题】:Indeterminate checkbox in React JSXReact JSX 中的不确定复选框
【发布时间】:2015-08-21 11:43:20
【问题描述】:

如何通过 JSX 渲染不确定的复选框?

这是我尝试过的:

function ICB({what}) {
  return <input type="checkbox"
                checked={what === "checked"} 
                indeterminate={what === "indeterminate"} />;
}

但是,indeterminate 不是HTMLElement 的属性,而是一个属性。如何从 React / JSX 设置属性?


解决方案

由于下面的大多数答案都使用 findDOMNodestring refs,这两者在 React 中不再被认为是好的做法,所以我编写了一个更现代的实现:

function ICB() {
  const [state, setState] = React.useState(0);
  const indetSetter = React.useCallback(el => {
    if (el && state === 2) {
      el.indeterminate = true;
    }
  }, [state]);
  const advance = () => setState(prev => (prev + 1) % 3);
  
  return <input type="checkbox"
                checked={state === 1}
                ref={indetSetter}
                onClick={advance} />;
}
                
ReactDOM.render(<ICB />, document.getElementById("out"));
<div id="out"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

【问题讨论】:

  • 你好。我尝试使用您的解决方案来解决我的问题(嵌套复选框),但它一直返回 null。你能帮忙解决一下吗?

标签: javascript reactjs


【解决方案1】:

也可以直接使用ref函数:

ReactDOM.render(
  <label>
    <input
      type="checkbox"
      ref={input => {
        if (input) {
          input.indeterminate = true;
        }
      }}
    />
    {' '}
    Un test
  </label>,
  document.getElementById('root')
);
<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>

【讨论】:

  • 谁否决了这个答案?!这是迄今为止最简单、最直接的解决方案。
【解决方案2】:

我可能会创建一个复合组件,它封装了设置或取消设置复选框的indeterminate 属性所需的钩子。看起来您使用的是 ES2015 语法,所以我将在此处使用其中的一些功能。

class IndeterminateCheckbox extends React.Component {
  componentDidMount() {
    if (this.props.indeterminate === true) {
      this._setIndeterminate(true);
    }
  }

  componentDidUpdate(previousProps) {
    if (previousProps.indeterminate !== this.props.indeterminate) {
      this._setIndeterminate(this.props.indeterminate);
    }
  }

  _setIndeterminate(indeterminate) {
    const node = React.findDOMNode(this);
    node.indeterminate = indeterminate;
  }

  render() {
    const { indeterminate, type, ...props } = this.props;
    return <input type="checkbox" {...props} />;
  }
}

// elsewhere

render() {
  return <IndeterminateCheckbox
           checked={this.props.state === "checked"} 
           indeterminate={this.props.state === "indeterminate"} />
}

工作示例:https://jsbin.com/hudemu/edit?js,output

【讨论】:

  • 谢谢。现在我认为indeterminate 应该是 HTML 中的一个属性,但这是一个可靠的解决方案。我会像他们一样接受@manji 的回答
  • 请注意,React.findDOMNode 自 0.14 起已弃用。请改用 react-dom 包中的 ReactDOM.findDOMNode
  • findDOMNode 好像已经被弃用了。
  • @LushaLi 是的,这篇文章已经很老了——现在你会使用 ref 来查找 DOM 节点
【解决方案3】:

您可以使用componentDidMount 步骤(在初始渲染后调用)来设置该属性:

componentDidMount() {
    React.findDOMNode(this).indeterminate = this.props.state === "indeterminate";
}

如果您希望在后续渲染中更新该属性,请在 componentDidUpdate 中执行相同操作。

【讨论】:

  • 那不会对新道具做出反应,我想我可以使用componentDidUpdate。但这不是在初始渲染时运行的......有没有优雅的方式?
  • componentDidUpdate 在第一次渲染时不会被调用,componentDidMount 在后续渲染中不会被调用。您必须同时使用两者。
  • 看起来 React 允许自定义 data- 属性,但没有别的:facebook.github.io/react/docs/… 这是他们正在跟踪一般自定义属性支持的 GitHub 问题:github.com/facebook/react/issues/140 现在,findDOMNode 似乎成为最好的解决方法。
  • 不是很漂亮,但你总是可以有两个输入,一个有一个没有,如果不确定状态为真,则返回第一个,如果为假,则返回另一个..
  • 当你需要对 dom 节点进行操作时,React 就是这样工作的。
【解决方案4】:

我建议创建一个简单的组件(代码从 coffeescript 移植过来,请注意,可能有一些简单的拼写错误):

const React = require('react');

module.exports = class IndeterminateCheckbox extends React.Component {
    componentDidMount() {
        this.refs.box.indeterminate = this.props.indeterminate;
    }

    componentDidUpdate(prevProps, prevState) {
        if(prevProps.indeterminate !== this.props.indeterminate) {
            this.refs.box.indeterminate = this.props.indeterminate;
        }
    }

    render() {
        return <input {...this.props} ref="box" type="checkbox"/>;
    }

}

现在你有一个简单的组件,其行为与复选框完全一样,它支持 indeterminate 属性。请注意,这里还有很大的改进空间,即为某些 props 设置 propTypes 和适当的默认值,当然还有实现 componentShouldUpdate 以仅在需要时执行某些操作。

【讨论】:

    【解决方案5】:

    另一种方法是使用带有回调的ref 属性来设置DOM 节点上的属性。例如:

    render: function() {
      return (
        <input
            type="checkbox"
            ref={function(input) {
              if (input != null) {
                React.findDOMNode(input).indeterminate = this.props.indeterminate;
              }}
            {...this.props} />
      )
    }
    

    【讨论】:

    • 拒绝投票,因为使用 ref 您已经可以访问 DOM 节点。不用React.findDOMNode(input)
    【解决方案6】:

    不要使用 React.findDOMNode(this)。 这是有风险的。

    export class SelectAll extends Component {
    constructor(props) {
        super(props);
        this.state = {
            checked: false
        };
        this.myRef = React.createRef();
        this.onChange = this.onChange.bind(this);
        this.update = this.update.bind(this);
    }
    
    onChange(e) {
        const checked = e.target.checked;
        this.setState({
            checked: checked
        });
        this.selectAllNode.indeterminate = false;
    }
    
    update(state: {
        checked: Boolean,
        indeterminate: Boolean
    }) {
        this.setState({
            checked: state.checked
        });
        this.myRef.current.indeterminate = state.indeterminate;
    }
    
    render() {
        return ( <
            input type = "checkbox"
            name = "selectAll"
            checked = {
                this.state.checked
            }
            onChange = {
                this.onChange
            }
            ref = {
                this.myRef
            }
            />
        );
    }
    }
    

    【讨论】:

    • 你能帮忙把它转换/实现成反应功能组件吗?
    【解决方案7】:

    React v15 实现:

    import React from 'react';
    
    export default class Checkbox extends React.Component {
        componentDidMount() {
            this.el.indeterminate = this.props.indeterminate;
        }
    
        componentDidUpdate(prevProps, prevState) {
            if(prevProps.indeterminate !== this.props.indeterminate) {
                this.el.indeterminate = this.props.indeterminate;
            }
        }
    
        render() {
            const {indeterminate, ...attrs} = this.props;
            return <input ref={el => {this.el = el}} type="checkbox" {...attrs}/>;
        }
    }
    

    【讨论】:

      【解决方案8】:

      取自my tutorial,它展示了它如何与最近的 React 功能一起工作。我希望这对偶然发现这个老问题的人有所帮助:

      const App = () => {
        const [checked, setChecked] = React.useState(CHECKBOX_STATES.Empty);
       
        const handleChange = () => {
          let updatedChecked;
       
          if (checked === CHECKBOX_STATES.Checked) {
            updatedChecked = CHECKBOX_STATES.Empty;
          } else if (checked === CHECKBOX_STATES.Empty) {
            updatedChecked = CHECKBOX_STATES.Indeterminate;
          } else if (checked === CHECKBOX_STATES.Indeterminate) {
            updatedChecked = CHECKBOX_STATES.Checked;
          }
       
          setChecked(updatedChecked);
        };
       
        return (
          <div>
            <Checkbox
              label="Value"
              value={checked}
              onChange={handleChange}
            />
       
            <p>Is checked? {checked}</p>
          </div>
        );
      };
      
      const Checkbox = ({ label, value, onChange }) => {
        const checkboxRef = React.useRef();
       
        React.useEffect(() => {
          if (value === CHECKBOX_STATES.Checked) {
            checkboxRef.current.checked = true;
            checkboxRef.current.indeterminate = false;
          } else if (value === CHECKBOX_STATES.Empty) {
            checkboxRef.current.checked = false;
            checkboxRef.current.indeterminate = false;
          } else if (value === CHECKBOX_STATES.Indeterminate) {
            checkboxRef.current.checked = false;
            checkboxRef.current.indeterminate = true;
          }
        }, [value]);
       
        return (
          <label>
            <input ref={checkboxRef} type="checkbox" onChange={onChange} />
            {label}
          </label>
        );
      };
      

      【讨论】:

        猜你喜欢
        • 2014-10-30
        • 2017-09-11
        • 1970-01-01
        • 2013-05-23
        • 2021-03-25
        • 2021-11-16
        • 1970-01-01
        • 2014-03-11
        相关资源
        最近更新 更多