如何将引用分配给多个组件

IT技术 javascript reactjs
2021-03-09 19:07:22

我正在使用 React 来渲染多个数据array.map
如何禁用列表中单击的按钮?

这是我的代码:

  onRunClick(act, e) {
    this.refs.btn.setAttribute("disabled", true);
  }

  render () {
    return (
      <div>
        {
          this.state.acts.map((act) => {
            let boundActRunClick = this.onRunClick.bind(this, act);

            return (
              <p key={act._id}>
                Name: {act.name}, URL(s): {act.urls}
                <button ref='btn' onClick={boundActRunClick}>Run</button>
              </p>
            )
          })
        }
      </div>
    );
  }
}

使用refs不起作用......我认为我无法添加状态,因为有多个按钮。

4个回答

你应该使用ref callback而不是 ref 并且是的你需要多个 refs,一个数组应该是好的

根据文档:

React 支持可以附加到任何组件的特殊属性。ref 属性带有一个callback函数, callback在组件挂载或卸载后会立即执行。

当 ref 属性用于 HTML 元素时,它ref callback 接收底层 DOM 元素作为其参数。

ref callbackscomponentDidMountcomponentDidUpdate生命周期钩子之前调用

使用 ref 回调只是为了在类上设置属性是访问 DOM 元素的常见模式。首选方法是在 ref 回调中设置属性,如上例所示。甚至还有一种更短的写法:ref={input => this.textInput = input}.

字符串引用是遗留的,并且根据文档

旧 API:字符串引用

如果您之前使用过 React,您可能熟悉旧 API,其中 ref 属性是一个字符串,如“textInput”,并且 DOM 节点作为this.refs.textInput. 我们建议不要这样做,因为字符串引用有一些问题,被认为是遗留的,并且可能会在未来的版本之一中被删除。如果您当前正在使用this.refs.textInput访问refs,我们建议改用回调模式。

constructor() {
    super();
    this.btn = [];
}
onRunClick(act, index, e) {
    this.btn[index].setAttribute("disabled", true);
  }

  render () {
    return (
      <div>
        {
          this.state.acts.map((act, index) => {
            let boundActRunClick = this.onRunClick.bind(this, act, index);

            return (
              <p key={act._id}>
                Name: {act.name}, URL(s): {act.urls}
                <button ref={(el) => this.btn[index] = el} onClick={boundActRunClick}>Run</button>
              </p>
            )
          })
        }
      </div>
    );
  }
在 React 16.3 中,我们在构造函数中使用了 createRef,这将如何应用在这里?
2021-04-28 19:07:22
这有一个小的泄漏问题:this.btn数组长度在收缩时永远不会this.state.acts收缩。此外,这为每次渲染的每个按钮提供了一个新的 ref 函数,因此在每次重新渲染时都会执行每个按钮的先前和新的 ref 函数。
2021-05-11 19:07:22

就像@ShubhamKhatri 的回答一样,使用ref是一种选择。您也可以通过状态实现所需的行为。

示例 (单个禁用按钮选项)

class App extends Component{
  constructor() {
    super();
    this.state = {
      disabled: ''
    };
  }

  onRunClick(act, index, e) {
    this.setState({ disabled: act._id });
  }

  render() {
    return (
      <div>
        {
          this.state.acts.map((act, index) => {
            let boundActRunClick = this.onRunClick.bind(this, act, index);
            return (
              <p key={act._id}>
                Name: {act.name}, URL(s): {act.urls}
                <button disabled={this.state.disabled === act._id} onClick={boundActRunClick}>Run</button>
              </p>
            )
          })
        }
      </div>
    );
  }
}

示例 (多个禁用按钮选项)

class App extends Component{
  constructor() {
    super();
    this.state = {
      buttons: {}
    };
  }

  onRunClick(act, index, e) {
    this.setState((prevState) => { 
      const buttons = Object.assign({}, prevState.buttons, { [act._id]: !prevState.buttons[act._id] });
      return { buttons };
    });
  }

  render() {
    return (
      <div>
        {
          this.state.acts.map((act, index) => {
            let boundActRunClick = this.onRunClick.bind(this, act, index);
            return (
              <p key={act._id}>
                Name: {act.name}, URL(s): {act.urls}
                <button disabled={this.state.buttons[act._id] || false} onClick={boundActRunClick}>Run</button>
              </p>
            )
          })
        }
      </div>
    );
  }
}

对于函数组件(React 16+),你可以像下面这样处理它:

/* 
 * @param {Object|Function} forwardedRef callback ref function  or ref object that `refToAssign` will be assigned to
 * @param {Object} refToAssign React ref object
 */
export function assignForwardedRefs(forwardedRef, refToAssign) {
  if (forwardedRef) {
    if (typeof forwardedRef === 'function') {
      forwardedRef(refToAssign)
    } else {
      forwardedRef.current = refToAssign
    }
  }
}


function MyComponent({
  forwardedRef
}) {
   const innerRef = useRef()

   function setRef(ref) {
     assignForwardedRefs(forwardedRef, ref)
     innerRef.current = ref
   }

   return <div ref={setRef}>Hello World!</div>
}

export default React.forwardRef((props, ref) => <MyComponent {...props} forwardedRef={ref} />)

你可以使用 npm modulereact-multi-ref(我的一个小库)来做到这一点。

import React from 'react';
import MultiRef from 'react-multi-ref';

class Foo extends React.Component {
  _actRefs = new MultiRef();

  onRunClick(act, e) {
    this._actRefs.map.get(act._id).setAttribute("disabled", true);
  }

  render () {
    return (
      <div>
        {
          this.state.acts.map((act) => {
            let boundActRunClick = this.onRunClick.bind(this, act);

            return (
              <p key={act._id}>
                Name: {act.name}, URL(s): {act.urls}
                <button ref={this._actRefs.ref(act._id)} onClick={boundActRunClick}>Run</button>
              </p>
            )
          })
        }
      </div>
    );
  }
}

尽管在这种特定情况下,您只想更改元素上的属性,而不是使用 ref,您应该<button>通过 React上的 state 和 props 来完成,如@bennygenel 的回答所示但是,如果您需要做其他事情(在按钮上调用命令式 DOM 方法、读取不受控制的输入元素的值、读取元素的屏幕位置等),那么您将需要使用这样的 ref。