AJAX callback on form item generated by AJAX
The Link 4k4 posted in the discussion of the question gives the solution:


  Create a callback function (named by the #ajax['callback']). This is generally a very simple function which does nothing but select and return the portion of the form that is to be replaced on the original page. Note, that as part of the Form APIs security system, you cannot create new form elements in the callback function, as they will throw errors upon submission, and any #ajax on elements created in the callback will also not work. If you need to create new elements on ajax submit, they must be added in the form definition. You can use the values in $form_state to determine whether the form build process is the initial form load, or an #ajax initiated load.


So the following code example is working.

class ReservationForm extends FormBase {

  public function getFormId() {
    return 'reservation_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['month'] = [
      '#type' =&gt; 'select',
      '#title' =&gt; $this-&gt;t('Select month'),
      '#options' =&gt; [
        $this-&gt;t('January'),
        $this-&gt;t('February'),
        $this-&gt;t('March'),
      ],
      '#ajax' =&gt; [
        'callback' =&gt; [$this, 'extraField'],
        'event' =&gt; 'change',
        'wrapper' =&gt; 'week-day',
      ],
    ];

    // Disable caching on this form.
    $form_state-&gt;setCached(FALSE);

    $form['week_day'] = [
      '#type' =&gt; 'container',
      '#attributes' =&gt; ['id' =&gt; 'week-day'],
    ];

    if ($form_state-&gt;getUserInput()['_triggering_element_name'] == 'month') {
      $month = $form_state-&gt;getValue('month');
      $hour_options = [];

      if ($month == '0') {
        $hour_options[0] = $this-&gt;t('08:00');
        $hour_options[1] = $this-&gt;t('09:00');
        $hour_options[2] = $this-&gt;t('10:00');
      }

      if ($month == '1') {
        $hour_options[0] = $this-&gt;t('12:00');
        $hour_options[1] = $this-&gt;t('13:00');
        $hour_options[2] = $this-&gt;t('14:00');
      }

      if ($month == '2') {
        $hour_options[0] = $this-&gt;t('18:00');
        $hour_options[1] = $this-&gt;t('19:00');
        $hour_options[2] = $this-&gt;t('20:00');
      }

      $form['week_day']['hour'] = [
        '#type' =&gt; 'select',
        '#title' =&gt; $this-&gt;t('Choose hours @month', ['@month' =&gt; $month]),
        '#options' =&gt; $hour_options,
        '#ajax' =&gt; [
          'callback' =&gt; [$this, 'choosePerson'],
          'event' =&gt; 'change',
          'wrapper' =&gt; 'person-container',
        ],
      ];

      $form['week_day']['person_container'] = [
        '#type' =&gt; 'container',
        '#attributes' =&gt; ['id' =&gt; 'person-container'],
      ];

      $form['week_day']['person_container']['person'] = [
        '#type' =&gt; 'select',
        '#title' =&gt; $this-&gt;t('Choose person'),
        '#options' =&gt; ['John', 'Sarah', 'Peter'],
      ];
    }

    if ($form_state-&gt;getUserInput()['_triggering_element_name'] == 'hour') {
      $form['week_day']['person_container']['person'] = [
        '#type' =&gt; 'select',
        '#title' =&gt; $this-&gt;t('Choose person'),
        '#options' =&gt; ['Hans', 'Frank', 'Emma'],
      ];
    }

    return $form;
  }

  public function submitForm(array &amp;$form, FormStateInterface $form_state) {
  }

  public function extraField(array &amp;$form, FormStateInterface $form_state) {
    return $form['week_day'];
  }

  public function choosePerson(array &amp;$form, FormStateInterface $form_state) {
    return $form['week_day']['person_container'];
  }

}