несколько уровней forwardRef и useImperativeHandle - PullRequest
3 голосов
/ 13 июля 2020

Мне нужно использовать ref, чтобы получить состояние компонента внука, и я настроил его, используя useImperativeHandle, весь код упрощен здесь .

function App() {
  const ref = useRef(null);
  return (
    <div className="App">
      <button
        onClick={() => console.log(ref.current.name, ref.current.age)}
      >click me</button>
      <FilterFather ref={ref} />
    </div>
  );
}

const FilterFather = (_, ref) => {
  const filter1Ref = useRef(null);
  const filter2Ref = useRef(null);
  useImperativeHandle(ref, () => ({
    name: filter1Ref.current.name,
    age: filter2Ref.current.age,
  }))
  return (
    <>
      <Filter1 ref={filter1Ref}/>
      <Filter2 ref={filter2Ref} />
    </>
  )
}
export default forwardRef(FilterFather);
const Filter1 = (props, ref) => {
  const [name, setName] = useState('lewis')
  useImperativeHandle(ref, () => ({
    name
  }), [name])
  return (
    <>
      <div>
        name:
        <input 
          value={name}
          onChange={e => setName(e.target.value)}
        />
      </div>
    </>
  )
}
const Filter2 = (props, ref) => {
  const [age, setAge] = useState(18)
  useImperativeHandle(ref, () => ({
    age
  }), [age])
  return (
    <>
      <div>
        age:
        <input 
          value={age}
          onChange={e => setAge(e.target.value)}
        />
      </div>
    </>
  )
}
export default {
  Filter1: forwardRef(Filter1),
  Filter2: forwardRef(Filter2),
}

один слой forwardRef и useImperativeHandle работает нормально, два слоя пошли не так

1 Ответ

1 голос
/ 13 июля 2020

Ваш императивный дескриптор в FilterFather не требуется. Он ничего не добавляет / не удаляет на ручку. Вы можете просто переслать его напрямую:

const FilterFather = (_, ref) => {
  return <Filter ref={ref} />;
};

Edit useImperativeHandle

Also there is a problem with it because it will not update correctly.

useImperativeHandle(ref, () => ({
    name: filterRef.current.name,
    age: filterRef.current.age,
}), [filterRef]) // this will not update correctly

You passed filterRef as a dependency but filterRef is static and will not change even when filterRef.current.name or filterRef.current.age changes.

Note that useImperativeHandle is not supposed to be used to read state from child components. It is actually discouraged to use any kind of imperative methods in react except there is no other way. This is most of the time the case if you work with 3rd party libraries that are imperative in nature.

EDIT:

Given your updated code the react way to do that is to lift state up. It doesn't require any refs:

export default function App() {
  const [values, setValues] = useState({
    name: "lewis",
    age: 18
  });

  const handleChange = useCallback(
    (key, value) => setValues(current => ({ ...current, [key]: value })),
    []
  );

  return (
     console.log (values.name, values.age)}> щелкните меня  ); } 
const FilterFather = props => {
  return (
    <>
      <Filter label="Name" name="name" {...props} />
      <Filter label="Age" name="age" {...props} />
    </>
  );
};
const Filter = ({ label, name, values, onChange }) => {
  const handleChange = useCallback(e => onChange(name, e.target.value), [
    name,
    onChange
  ]);

  return (
    <>
      <div>
        <label>{label}:</label>
        <input value={values[name]} onChange={handleChange} />
      </div>
    </>
  );
};

Изменить useImperativeHandle

...