← Назад к вопросам

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

1.8 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Передача параметров из дочернего компонента в родительский в React

В React данные обычно текут "сверху вниз" через props, но часто нужно передать данные "снизу вверх" из дочернего компонента. Для этого существует несколько паттернов.

1. Callback функции (Lifting State Up)

Это основной и рекомендуемый способ.

// Родительский компонент
function Parent() {
  const [selectedColor, setSelectedColor] = useState('blue');
  
  // Функция для получения данных от дочернего
  const handleColorChange = (color) => {
    setSelectedColor(color);
  };
  
  return (
    <div>
      <p>Выбранный цвет: {selectedColor}</p>
      {/* Передаём callback в дочерний */}
      <ColorPicker onColorChange={handleColorChange} />
    </div>
  );
}

// Дочерний компонент
function ColorPicker({ onColorChange }) {
  const colors = ['red', 'green', 'blue', 'yellow'];
  
  return (
    <div>
      {colors.map(color => (
        <button
          key={color}
          onClick={() => onColorChange(color)}  // Отправляем данные вверх
        >
          {color}
        </button>
      ))}
    </div>
  );
}

2. Callback с объектом параметров

Передай несколько значений одновременно.

function Parent() {
  const [userInfo, setUserInfo] = useState({
    name: '',
    age: 0,
    email: ''
  });
  
  const handleUserUpdate = (data) => {
    setUserInfo(prev => ({
      ...prev,
      ...data  // Merge новых данных
    }));
  };
  
  return (
    <div>
      <p>Name: {userInfo.name}</p>
      <p>Age: {userInfo.age}</p>
      <UserForm onUpdate={handleUserUpdate} />
    </div>
  );
}

function UserForm({ onUpdate }) {
  const [name, setName] = useState('');
  const [age, setAge] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    // Отправляем объект с несколькими параметрами
    onUpdate({ name, age: parseInt(age) });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={e => setName(e.target.value)} />
      <input value={age} onChange={e => setAge(e.target.value)} />
      <button type="submit">Update</button>
    </form>
  );
}

3. Context API для глубокой иерархии

Если данные нужно передать через много уровней, используй Context.

// Создаём Context
const UserContext = React.createContext();

function App() {
  const [user, setUser] = useState({ name: '', email: '' });
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      <ParentLevel1 />
    </UserContext.Provider>
  );
}

// Где угодно в дереве можем получить доступ
function DeepChild() {
  const { user, setUser } = useContext(UserContext);
  
  return (
    <input
      value={user.name}
      onChange={e => setUser({ ...user, name: e.target.value })}
    />
  );
}

4. useReducer для сложной логики

Когда state очень сложный или много действий.

function Parent() {
  const initialState = {
    items: [],
    loading: false,
    error: null
  };
  
  function reducer(state, action) {
    switch (action.type) {
      case 'ADD_ITEM':
        return {
          ...state,
          items: [...state.items, action.payload]
        };
      case 'REMOVE_ITEM':
        return {
          ...state,
          items: state.items.filter(item => item.id !== action.payload)
        };
      case 'SET_LOADING':
        return { ...state, loading: action.payload };
      default:
        return state;
    }
  }
  
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    <div>
      {state.items.map(item => (
        <ItemComponent
          key={item.id}
          item={item}
          onRemove={() => dispatch({ type: 'REMOVE_ITEM', payload: item.id })}
        />
      ))}
      <ItemForm onAdd={(item) => dispatch({ type: 'ADD_ITEM', payload: item })} />
    </div>
  );
}

5. Refs (не рекомендуется, но иногда нужно)

Для non-controlled компонентов или интеграции с сторонними библиотеками.

function Parent() {
  const childRef = useRef();
  
  const handleGetData = () => {
    // Прямой доступ к методам дочернего компонента
    const data = childRef.current.getData();
    console.log(data);
  };
  
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={handleGetData}>Get Data</button>
    </div>
  );
}

const Child = forwardRef((props, ref) => {
  const [data, setData] = useState('Hello');
  
  useImperativeHandle(ref, () => ({
    getData: () => data
  }));
  
  return <div>{data}</div>;
});

6. Custom Hooks для переиспользуемой логики

// Custom hook для управления состоянием
function useFormInput(initialValue = '') {
  const [value, setValue] = useState(initialValue);
  
  return {
    value,
    setValue,
    bind: {
      value,
      onChange: e => setValue(e.target.value)
    }
  };
}

// Использование в родителе
function Parent() {
  const nameInput = useFormInput('');
  const emailInput = useFormInput('');
  
  const handleSubmit = () => {
    console.log('Name:', nameInput.value);
    console.log('Email:', emailInput.value);
  };
  
  return (
    <div>
      <Child
        nameInput={nameInput}
        emailInput={emailInput}
        onSubmit={handleSubmit}
      />
    </div>
  );
}

// В дочернем компоненте
function Child({ nameInput, emailInput, onSubmit }) {
  return (
    <form onSubmit={e => { e.preventDefault(); onSubmit(); }}>
      <input {...nameInput.bind} placeholder="Name" />
      <input {...emailInput.bind} placeholder="Email" />
      <button type="submit">Submit</button>
    </form>
  );
}

7. Event Emitter паттерн (для сложных случаев)

// Создать простой event emitter
class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }
}

const emitter = new EventEmitter();

function Parent() {
  useEffect(() => {
    emitter.on('userUpdate', (data) => {
      console.log('User updated:', data);
    });
  }, []);
  
  return <Child />;
}

function Child() {
  return (
    <button onClick={() => emitter.emit('userUpdate', { name: 'John' })}>
      Update
    </button>
  );
}

Сравнение подходов

const comparison = {
  callbacks: {
    простота: "Очень простые в использовании",
    производительность: "Хорошая",
    масштабируемость: "Для небольших иерархий",
    лучше_всего: "Для передачи функций в дочерние компоненты"
  },
  
  context: {
    простота: "Требует setup",
    производительность: "Может быть медленнее при частых обновлениях",
    масштабируемость: "Отлично для глубокой иерархии",
    лучше_всего: "Для глобального состояния (theme, auth, язык)"
  },
  
  useReducer: {
    простота: "Сложнее, но мощнее",
    производительность: "Отличная",
    масштабируемость: "Для сложного state",
    лучше_всего: "Для сложной логики и множества actions"
  },
  
  refs: {
    простота: "Простое, но не React-way",
    производительность: "Лучшая",
    масштабируемость: "Не масштабируется",
    лучше_всего: "Редко, для DOM манипуляций"
  }
};

Best Practices

const bestPractices = [
  "1. Используй callbacks для простых случаев",
  "2. Не переплачивай렌更новлять родителя если не нужно (useMemo, useCallback)",
  "3. Для глубокой иерархии используй Context",
  "4. Избегай Refs если есть альтернатива",
  "5. Разделяй логику на Custom Hooks",
  "6. Лучше поднять state выше, чем пробрасывать много props"
];

Выбирай подход в зависимости от сложности: callbacks для простого, Context для глобального, useReducer для complex state.