Как реализовать пошаговый процесс принятия решений в JavaScript?

Я не совсем уверен в правильном названии того, что пытаюсь описать. Схема принятия решений, матрица решений, блок-схема ...

Как лучше всего реализовать поток, который по сути является шаговым двигателем «Выбери свое собственное приключение» или блок-схемой, превращенной в степпер?

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

(Я использую React для рендеринга компонентов на каждом этапе, но я не думаю, что это имеет большое значение в отношении вопроса).

renderStep = () => {
    switch (currentStep) {
      case 1:
        return <FirstStep/>
      case 2:
        return <SecondStep />
      case 2.5:
        return <SecondStepA />
      case 3:
        return <ThirdStep />
      case 3.25:
        return <ThirdStepA />
      case 3.5:
        return <ThirdStepB />
      case 3.75:
        return <ThirdStepC />
      case 4:
        return <FourthStep />
      default:
        return null
    }
  }

Теперь нажатие «Далее» или «Назад» отправит данные и определит для каждого шага, к какому шагу перейти. Если бы поток был линейным, это было бы очень просто - обновить данные, увеличить или уменьшить на единицу. Но с условным потоком все усложняется.

  goToPreviousStep = () => {
    const { currentStep } = this.state

    this.setState(prevState => {
      if (currentStep === 4 && prevState.someDataHappened) {
        return { currentStep: prevState.currentStep - 0.5 }
      } else if (currentStep === 3.5) {
        return { currentStep: prevState.currentStep - 0.5 }
      } else {
        return { currentStep: prevState.currentStep - 1 }
      }
    })
  }

Потенциально попытка поддержать дополнительное вложение помимо этого было бы еще более сложной задачей. По мере роста я знаю, что это станет недостижимым, но я не могу найти никаких хороших ресурсов для хранения этих данных в таблице или чего-то еще, чтобы сделать их более программными. Может ли кто-нибудь указать мне правильное направление?

Не уверен, что мне нужно написать какие-то недетерминированные конечные автоматы или что-то в этом роде ...


person Tania Rascia    schedule 04.12.2019    source источник
comment
Здесь Javascript ничем не отличается от любого другого языка, возможно, более мощного. Но как скажешь. Забудьте о презентационном слое. Вам нужны данные (JSON) и базовый rule класс. Отсюда вы можете выводить и создавать более сложные правила. Самым основным является этап обработки. Более сложным является правило ЕСЛИ. Этот список можно продолжить.   -  person Bibberty    schedule 04.12.2019
comment
@Bibberty предполагает, что каждый шаг определяется новым вызовом API для получения данных, а следующий определяется этими данными API ... Я бы не смог создать JSON заранее. Будет ли работать этот подход?   -  person Tania Rascia    schedule 04.12.2019
comment
Таня, вы создаете набор классов, начиная с базового класса, этот класс принимает данные от родителя и выполняет логику в производном классе. Это, в свою очередь, на основе реализованной логики влияет на данные и вызывает одного или нескольких их дочерних элементов.   -  person Bibberty    schedule 04.12.2019


Ответы (1)


Вы видели что-то похожее на то, как работает связанный список? Каждый шаг - это объект, и каждый шаг имеет ссылку на свой предыдущий шаг, а также на возможные следующие шаги. Переход можно выполнить, перейдя по этим ссылкам, а текущий шаг можно сохранить как объект, а не какое-то число. Если вы хотите пронумеровать их, вы можете просто сосчитать все шаги, а затем вернуться к первому шагу, чтобы увидеть, какой номер шага вы находитесь.

Вы можете поместить в прототип шага столько данных, сколько вам нужно, чтобы каждый шаг определял все необходимое для его рендеринга, выполнения действий на его основе и т. Д. Заголовок, текст, связанные URL-адреса и т. Д.

function Step() {}

Step.prototype.previous = null;
Step.prototype.next = [];

Step.prototype.addNext = function(nextStep) {
  let step = Object.assign(new Step(), nextStep);
  step.previous = this;
  this.next.push(step);
}

Step.prototype.getNumber = function() {
  var number = 0;
  var step = this;
  
  while(step) {
    step = step.previous;
    number += 1;
  }
  
  return number;
}


let CurrentStep = null;
function createSteps() {

  const step1 = new Step();
  const step2 = new Step();
  const step2_1 = new Step();
  const step2_2 = new Step();
  const step3 = new Step();
  
  CurrentStep = step1;
  
  step1.addNext(step2);
  step1.addNext(step2_1);
  step1.addNext(step2_2);
  
  step2.addNext(step3);
  step2_1.addNext(step3);
  step2_2.addNext(step3);

}

createSteps();

console.log(CurrentStep.getNumber());
console.log(CurrentStep.next[0].getNumber());
console.log(CurrentStep.next[1].getNumber());
console.log(CurrentStep.next[2].getNumber());

person flagoworld    schedule 04.12.2019
comment
Похоже, это хороший подход. По-прежнему будет много логики, чтобы выяснить, к какому шагу перейти, но выяснить, какой следующий или предыдущий выбрать было бы лучше, чем вычитание и добавление 0,5 ... - person Tania Rascia; 04.12.2019
comment
Как бы вы перешли к (условному) следующему в этом примере? - person Tania Rascia; 04.12.2019
comment
Что вы имеете в виду под (условным) следующим? Можете быть более конкретными? - person flagoworld; 05.12.2019
comment
К сожалению, в этом подходе есть проблема с предыдущим. step1.addNext(step2) выполнит предыдущее из шага 2- ›шаг1, затем step1.addNext(step2_1) выполнит предыдущее из шага 2_1-› шаг1, затем step2.addNext(step3) выполнит предыдущее из шага 3- ›шаг2, затем step2_1.addNext(step3) выполнит предыдущее из шага 3-› шаг2_1, что означает, что если вы перешли к шагу 3 с шага 2, предполагается, что вы попали туда с шага 2_1. - person Tania Rascia; 05.12.2019
comment
Вы можете решить эту проблему, неглубоко копируя каждый шаг, когда он добавляется в addNext. Предыдущий также может быть массивом, если вам нужна дополнительная логика. Я отредактировал пример. - person flagoworld; 06.12.2019
comment
Спасибо, @flagoworld, в итоге я сделал нечто подобное и использовал массив для предыдущего. Это было очень полезно. - person Tania Rascia; 06.12.2019