简体   繁体   中英

How can I prevent XState node from resetting it's child parallel states when receiving an event?

I was exploring the XState world and tried to recreate the machine @davidkpiano mentioned in this talk

And faced an issue with doing the transitions right. When I send a message to parent machine, it resets all the child machines.

For example: I expect the machine to be in dirty and focused state after I fire the CHANGE and FOCUS event one after another. But after sending FOCUS message the pristine state resets back to pristine .

I found this issue , but multiple transitions do the same thing (as it actually described in the issue itself)

Also, I don't want to store all the information about pristine, focused and touched state into the context , because it will not be as safe as doing it with state machines.

The code below is copy-pastable into https://xstate.js.org/viz/

const createPristineMachineConfig = (inputName) => {
  return {
    id: `${inputName}.pristine`,
    initial: 'pristine',
    states: {
      pristine: {
        on: {
          [`${inputName}.CHANGE`]: 'dirty',
        },
      },
      dirty: {
        type: 'final',
      },
    },
  };
};

const createTouchedConfig = (inputName) => {
  return {
    id: `${inputName}.touched`,
    initial: 'untouched',
    states: {
      untouched: {
        on: {
          [`${inputName}.TOUCH`]: 'touched',
        },
      },
      touched: {
        type: 'final',
      },
    },
  };
};

const createFocusedMachineConfig = (inputName) => {
  return {
    id: `${inputName}.focused`,
    initial: 'blurred',
    states: {
      blurred: {
        on: {
          [`${inputName}.FOCUS`]: 'focused',
        },
      },
      focused: {
        on: {
          [`${inputName}.BLUR`]: 'blurred',
        },
      },
    },
  };
};

const createInputMachineConfig = (inputName) => ({
  id: inputName,
  type: 'parallel',
  context: {
    value: '',
  },
  on: {
    FOCUS: {
      actions: send(`${inputName}.FOCUS`),
      internal: true,
    },
    BLUR: {
      actions: [send(`${inputName}.TOUCH`), send(`${inputName}.BLUR`)],
      internal: true,
    },
    CHANGE: {
      actions: [assign((ctx, event) => ({ ...ctx, value: event.payload.value })), send(`${inputName}.CHANGE`)],
      internal: true,
    },
  },
  states: {
    pristine: createPristineMachineConfig(inputName),
    touched: createTouchedConfig(inputName),
    focused: createFocusedMachineConfig(inputName),
  },
});

const loginInputMachine = Machine(createInputMachineConfig('login'));

Maybe that happens because dirty is a final state, for example:

{
  id: 'loginForm',
  initial: 'idle',
  context: {
    error: undefined,
  },
  invoke: [
    {
      id: 'usernameInput',
      src: inputMachine,
    },
    {
      id: 'passwordInput',
      src: inputMachine,
    },
    {
      id: 'loginButton',
      src: buttonMachine,
    },
  ],
  states: {
    idle: {
    },
    processing: {

    },
    finished: {
    },
    failed: {
    },
  },
};

I have this machine which spawns some actors, and I can access through loginForm machine in the following way loginForm.state.children.usernamInput , but if one of those machines have reach a final state it returns undefined.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM