Как передать данные к более "древнему" родительскому компоненту?

Мне нужно, чтобы данные от дочернего компонента Li доходили до родительского компонента 3-4 уровня -- Li -> Menu -> App

Вот мой код, пытался использовать onChange, но он работает только для форм

export default function App(){
    const style = {
        maxWidth: 1200 + 'px',
        margin: '0 auto',
        padding: '0 15px'
    }

    const handleClick = (value: any) => {
        console.log(value);
    }

    return(
        <div style = {style} >
            <Menu test2 = {handleClick} />
        </div>
    ) 
}

function Menu(props: any){
    const [click , setClick] = useState('')

    const style = {
        margin: 0,
        padding: 0
    }


    const handleTest = () => {
        props.test2(click);
    }

    const handleClick = (value: any) => {
        setClick(value);
    }
    return (
        <nav data-test = {handleTest}>
            <ul style = {style}>
                <Li test = {handleClick}>hi</Li>
            </ul>
        </nav>
    )
}

function Li(props: any){
    const style = {
        listStyle: 'none',
        fontSize: 24 + 'px'
    }

    const [click, setClick] = useState('');
    const handleClick = () => {
        props.test('pidoras')
        setClick('hi');
    } 
    return (
        <li 
            style = {style}
            onClick = {handleClick}>
            {props.children} <br />
            {click}
        </li>
    )
}

Ответы (1 шт):

Автор решения: SwaD

Ниже пример, как можно пробрасывать функции из родительского компонента в дочерний любой вложенности.

По сути, надо просто передавать саму фукнцию в пропсы дочернего компонента.

Создадим родительский компонент, в котором будет состояние и рендер дочернего компонента. В дочерний компонент Second мы передаем фукнцию setVal в параметре click click={setVal}. Так же передадим значения для рендера(массив) renderData.

const App = () => {
  const [val, setVal] = useState("");
  const [renderData, setRenderData] = useState([
    { name: "one", value: 1 },
    { name: "two", value: 2 },
    { name: "three", value: 3 }
  ]);
  return (
    <div className="App">
      <Second click={setVal} renderData={renderData} />
      <hr />
      {val}
    </div>
  );
};

В компоненте Second мы принимам параметры click и renderData. В компоненте первый рендер массива передаем "как есть" переменную click(в ней лежит функция из App) и сам элемент в компонент Line. Во второй рендр(для примера) передадим фукнцию modify, которая будет добавлять 10 к полученному значению.

const Second = ({ clickб renderData }) => {
  const modify = (val) => {
    click(val + 10);
  };
  return (
    <>
      Пробрасываем функцию как есть:
      <br />
      {lines.map((item) => (
        <Line key={item.name} data={item} click={click} />
      ))}
      <br />
      Пробрасываем модифицированную функцию:
      <br />
      {lines.map((item) => (
        <Line key={item.name} data={item} click={modify} />
      ))}
    </>
  );
};

Сам компонент Line принимает на вход data(Данные для отрисовки) и click(функция, которая будет установлена для события onClick()

const Line = ({ data, click }) => {
  return <button onClick={() => click(data.value)}>{data.name}</button>;
};

Таким образом, мы протянули функцию через 2 компонента. При необходимости, ее можно и через 10 протянуть.

Работающий пример данного ответа в песочнице codesandbox

→ Ссылка