In actual react project development, it is sometimes necessary to call the child component method in the parent component, and the child component is generated in a loop. Although this does not conform to the react design philosophy, there is a requirement for this in development. Here are two solutions:

The first way is to useuseRef/useImperativeHandle/forwardRef:

  1. useRefInitialize to an array
  2. Set in child component loop generationchildRefs.current[item.id] = ref;
  3. Child components are requiredforwardRefWrap it up,(useconnectSubcomponents of the package need to be added{ forwardRef: true }See you,here)
  4. useuseImperativeHandleTo export the child component, note the second argument to the child component

Here is the simplest code and associated comments:

// Parent component:
import React, { useRef } from 'react';
import ChildComp from './ChildComp';
function App() {
  const arr = [
    {
      id: 1.name: '🍎'}, {id: 2.name: '🍌'}, {id: 3.name: '🍊',},];// useRef is initialized to an array
  const childRefs = useRef<any> ([]);return (
    <div className="App">
      {arr.map((item) => (
        <ChildComp/ / ⚠ ️ attentionrefIt could be empty so I'm going to add a judgment hereref={(ref)= > {
            if (ref) {
              childRefs.current[item.id] = ref;
            }
          }}
          data={item}
          key={item.id}
        />
      ))}
      <button
        onClick={()= >{childRefs. Current && childRefs. Current. ForEach ((childRef: any) = > {/ / call the method that all subcomponents childRef. ExportChildMethod (); / / get all subcomponents the value of the console, log (childRef. ExportChildValue ()); }); Console. log(childrefs.current && childrefs.current [1].exportChildValue()); }} > Invoke the child component's methods</button>
    </div>
  );
}
export default App;

/ / child component
import React, { forwardRef, useState, useImperativeHandle } from 'react';
// the component wrapped with the forwardRef gets a ref argument
const Child = (props: any, ref: any) = > {
  const { data } = props;
  const [childNum, setChildNum] = useState<number> (0);
  UseImperativeHandle exports the methods used by the parent component
  useImperativeHandle(ref, () = > ({
    exportChildMethod: () = > {
      childMethod();
    },
    exportChildValue: () = > {
      returnchildNum; }}));const childMethod = () = > {
    console.log(`log from child--${data.name}`);
  };
  return (
    <div>
      <p>{data.name}</p>
      <p>{childNum}</p>
      <button
        onClick={()= > {
          setChildNum(childNum + 1);
        }}
      >
        child add
      </button>
    </div>
  );
};
// Need to use forwardRef wrap
export default forwardRef(Child);
Copy the code

The second way is not to use ituseRef/forwardRef/useImperativeHandle, the idea is that props changes the method used to trigger a child component:

  1. The parent component sets a number of millisecondsmsIs empty
  2. The parent component triggers the action, setting the number of millisecondsmsfornew Date().getTime()
  3. themsPass to a child component
  4. Child components in theuseEffectThe dependencemsThe value of themsChange the function within the trigger child
import React from 'react';
import ChildComp from './ChildComp';
function App() {
  const arr = [
    {
      id: 1.name: '🍎'}, {id: 2.name: '🍌'}, {id: 3.name: '🍊',},];const [ms, setMs] = useState<number | undefined> ();const emitChildMethod = () = > {
    setMs(new Date().getTime());
  };
  return (
    <div className="App">
      {arr.map((item) => (
        <ChildComp ms={ms} data={item} key={item.id} />
      ))}
      <div>
        <button onClick={emitChildMethod}>Invoke methods of child components</button>
      </div>
    </div>
  );
}
export default App;

/ / child component
import React, { useState, useEffect } from 'react';
const Child = (props: any) = > {
  const { data } = props;
  const [childNum, setChildNum] = useState<number> (0);
  useEffect(() = > {
    // Make a judgment call here
    if (ms) {
      childMethod();
    }
  }, [ms]);
  const childMethod = () = > {
    console.log(`log from child--${data.name}`);
  };
  return (
    <div>
      <p>{data.name}</p>
      <p>{childNum}</p>
      <button
        onClick={()= > {
          setChildNum(childNum + 1);
        }}
      >
        child add
      </button>
    </div>
  );
};
export default Child;
Copy the code

To summarize

  1. Both approaches are a last resort and break the design pattern for single data flows
  2. It is also bad for code debugging and business sorting
  3. However, it has certain advantages in modifying old code and adding new functions