Skip to content

Instantly share code, notes, and snippets.

@Akiyamka
Last active May 22, 2020 11:06
Show Gist options
  • Save Akiyamka/72a90a9dc58143aad396584509d647fc to your computer and use it in GitHub Desktop.
Save Akiyamka/72a90a9dc58143aad396584509d647fc to your computer and use it in GitHub Desktop.
How you call children methods from parrent?

Как вы решаете задачу - вызова функции внутри дочернего компонента из родителя?

Представим что у нас есть приложение вида:

<App>
  <Widget /> 
</App>

Где <Widget /> это компонент имеющий своё внутренее состояние которое мы хотим сбросить.

Какие есть варианты?

1. Boolean пропс Widget-a тригерит функцию reset внутри него, когда isReset == true.

const [widgetReset, setWidgetReset] = useState(false);

// some if
setWidgetReset(true);

return <Widget isReset={widgetReset} />

Однако чтобы это работало более одного раза нам надо или сделать хитрую функцию установки значения:

const [triggerReset, setTriggerReset] = useState();
const resetWidget = useCallback(
  () => setTriggerReset(Performance.now()),
  [setTriggerReset]
);

// some if
resetWidget();

return <Widget triggerReset={triggerReset} />

Или добавить пропс onReset для возврата в исходное состояние:

const [widgetReset, setWidgetReset] = useState(false);
const onReset = useCallback(
  () => setWidgetReset(false),
  [setWidgetReset]
);

// some if
setWidgetReset(false)


return  <Widget isReset={widgetReset} onReset={onReset}/>

2. Добавить рендер проп который будет отдавать ссылку на reset функцию

const resetWidgetRef = useRef();
const resetWidget = useCallback(
  () => typeof resetWidgetRef.current === 'function' && resetWidgetRef.current(),
  [resetWidgetRef]
);

// some if
resetWidget()

return (
  <Widget>
  { (reset) => resetWidgetRef.current = reset }
  </Widget>
)

3. Использовать useImperativeHandle

В этом случае сперва нужно сделать forwardRef обертку над Widget компонентом

function Widget(props, ref) {
  const [state, setState] = useState(initialState);

  useImperativeHandle(ref, () => ({
    reset: () => setState(initialState)
  }));

  return <div>{ /* some JSX */ }</div>;
}

const ForwardedWidget = forwardRef(FancyInput);
export ForwardedWidget;

Далее используем как:

const widgetRef = useRef();
const resetWidget = useCallback(
  () => typeof widgetRef.current?.reset === 'function' && widgetRef.current.reset(),
  [resetWidgetRef]
);

// some if
resetWidget()

return (
  <Widget ref={widgetRef} />
)
@Buggytheclown
Copy link

Если нужно сбросить состояние дочернего компонента - дестрою старый, создаю новый (в React установить новый 'key', angular немного танцев с "trackBy")

@Buggytheclown
Copy link

Когда нельзя, но очень надо, еще варианты:

  • Передать дочернему "EventEmitter" на который он подпишется, парент будет эмитить сообщения

@Akiyamka
Copy link
Author

Akiyamka commented May 22, 2020

Интересный способ с key, может пригодится когда компонент сторонний и не поддерживает специальных апи для сброса своего состояния

@Buggytheclown
Copy link

Интересный способ с key, может пригодится когда компонент сторонний и не поддерживает специальных апи для сброса своего состояния

Воспринимаю этот способ как рекомендуемый (вроде где-то около офф доки видел), дергать методы дочернего - моветон (unidirectional data flow - data down, events up)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment