多层forwardRef和useImperativeHandle

时间:2020-07-13 08:20:33

标签: reactjs react-hooks

我需要使用ref来获取孙子组件的状态,并通过使用useImperativeHandle自定义它,简化了整个代码 here

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),
}

forwardRefuseImperativeHandle的一层工作正常,两层出错了

1 个答案:

答案 0 :(得分:1)

您不需要FilterFather中的命令句柄。它不会向句柄添加/删除任何内容。您可以直接转发它:

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

Edit useImperativeHandle

还有一个问题,因为它不会正确更新。

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

您通过了filterRef作为依赖项,但是filterRef是静态的,即使filterRef.current.namefilterRef.current.age发生更改也不会更改。

请注意,useImperativeHandle不应用于从子组件中读取状态。实际上,不鼓励在反应中使用任何命令式方法,除非没有其他方法。在大多数情况下,如果您使用本质上必不可少的第三方库,就属于这种情况。

编辑

给出更新后的代码,可以做到的是提高状态。它不需要任何参考:

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

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

  return (
    <div className="App">
      <button onClick={() => console.log(values.name, values.age)}>
        click me
      </button>
      <FilterFather values={values} onChange={handleChange} />
    </div>
  );
}
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>
    </>
  );
};

Edit useImperativeHandle