如何从父组件调用同一个子组件函数的多个实例

How to call multiple instances of the same child component's function from parent

我知道使用 ref 可以从父组件调用子组件中的函数。但我的问题是我有子组件的多个实例,我需要在所有子组件中调用相同的函数。

// Get a hook function
const {useState, useRef, useImperativeHandle, useEffect} = React;

const Parent = ({title}) => {

  const childRef1 = useRef();
  const childRef2 = useRef();
  const childRef3 = useRef();
  const childRef4 = useRef();
  const childRef5 = useRef();
  const handleClick = () => {
  
    childRef1.current.increment()
    childRef2.current.increment()
    childRef3.current.increment()
    childRef4.current.increment()
    childRef5.current.increment()
  }
  
  return (
    <div>
      <p>{title}</p>
      <Child num={1} childRef={childRef1} />
      <Child num={2} childRef={childRef2} />
      <Child num={3} childRef={childRef3} />
      <Child num={4} childRef={childRef4} />
      <Child num={5} childRef={childRef5} />
      <button onClick={() => handleClick()}>
        Click me
      </button>
    </div>
  );
};

const Child = ({num, childRef}) => {
  
  const [count, setCount] = useState(0)
  
  useImperativeHandle(childRef, () => ({
    increment() {
      // if(textEditable)
      setCount(count+1)
    },
  }));
  
  useEffect(() => {
    setCount(num)
  }, [num])
  
  return (
    <div>
    {
      count
    }
    </div>
  )
}

// Render it
ReactDOM.render(
  <Parent title="Example using Hooks:" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

我的问题是,如果我在 map 函数中渲染子组件,我应该怎么做?

跟踪父级中的状态并将其作为道具传递给子级:

const Parent = ({title}) => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{title}</p>
      <Child count={count} />
      <Child count={count} />
      <Child count={count} />
    </div>
  )
}

如果你需要它们是独立的,使用状态数组:

const Parent = ({title}) => {
  const [count, setCount] = useState([0,1,42,96]);

  const onIncrement = index => {
    // probably a better way to do this. just here for illustrative purposes
    const newCount = [...count];
    newCount.splice(index, 1, count[index] + 1);
    setCount(newCount);
  }

  const incrementAll = () => setCount(count.map(x => x + 1));

  return (
    <div>
      <p>{title}</p>
      { count.map((entry, index) => (
        <Child
          num={entry}
          key={index}
          onIncrement={() => onIncrement(index))} {/* if you want the child to be able to do it */}
        />
      )) }
      <button onClick={incrementAll}>Click Me</button>
    </div>
  )
}

I know that using ref's I can call a function...

完美!在那种情况下,我们将使用相同的过程,但只是进一步扩展它。

设置组件或状态来保存子组件...

this.state.children = [];

然后map()他们就这样...

var children = [1,2,3,4,5,6];
children.map((child) => {
    return (
        <Child
            num={child}
            ref={(instance) => {this.state.children[child] = instance}}
        />
    );
});

然后调用带有 forEach() 的函数...

Object.keys(this.state.children).forEach((child) => {
    var childinstance = this.state.children[child];
    childinstance.specialFunction();
});

我是用下面的钩子做的。

// Get a hook function
const {useState, useRef, useImperativeHandle, useEffect} = React;

const Parent = ({title}) => {
  const [count, setCount] = useState(0);
  const [children, setChildren] = useState(5);
  
  const [childRefs, setChildRefs] = useState([])
  
  const handleClick = () => childRefs.forEach(c => c.current.increment())
  
  useEffect(() => {
    
    setChildRefs([...Array(children).keys()].map(e => React.createRef()))
  }, [title])
  
  return (
    <div>
      <p>{title}</p>
      {
        [...Array(children).keys()].map(e => <Child
            num={e}
            childRef={ childRefs[e] }
        />)
      }
      <button onClick={() => handleClick()}>
        Click me
      </button>
    </div>
  );
};

const Child = ({num, childRef}) => {
  
  const [count, setCount] = useState(0)
  
  useImperativeHandle(childRef, () => ({
    increment() {
      // if(textEditable)
      setCount(count+1)
    },
  }));
  
  useEffect(() => {
    setCount(num)
  }, [num])
  
  return (
    <div>
    {
      count
    }
    </div>
  )
}

// Render it
ReactDOM.render(
  <Parent title="Example using Hooks:" />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>