Martin
Martin

Reputation: 6015

How do I sequence HTTP requests in a Cycle.JS login form component?

I am new to Cycle.JS. Please forgive the typescript errors as well! I'm using create-cycle-app.

I made a simple login form component. It uses the DOM and HTTP drivers. I capture the submit event from the form and map it to a credential object containing the entered username and password. I have a throttledCredential stream. It uses fold to keep track of the previous credentials and HTTP responses. It should only emit credentials if the previous event was a failed response.

I have a working throttledCredentials stream, but this took me a long time to come up with and I find it very hard to read. Is there a better way to do this?

function main(sources: Sources): Sinks {
    // emits credentials whenever the the form is submitted
    const credentials$: Stream<Credentials> = sources.DOM
        .select('.form')
        .events('submit', { preventDefault: true })
        .map((e: any) => ({
            username: e.target.elements.username.value,
            password: e.target.elements.password.value,
        }))

    // maps the response onto a boolean success === true or failure === false
    const response$: Stream<boolean> = sources.HTTP
        .select('login')
        .flatten()
        .map(response => response.ok)

    // can this be simplified?
    const throttledCredentials$: Stream<Credentials> = xs
        .merge(
            credentials$.map(credentials => ({ credentials, response: null })),
            response$.map(response => ({ credentials: null, response }))
        )
        .fold((prev: any, { credentials, response }) => {
            return { credentials, cur: (credentials || response) ? true : false, prev: prev.cur }
        }, { credentials: null, cur: false, prev: false })
        .filter((x: any) => x.cur === true && x.prev === false)
        .map(x => x.credentials)

    // maps emitted credentials onto an HTTP RequestOptions to be POSTed
    const request$: Stream<RequestOptions> = throttledCredentials$
        .map(credentials => ({
            url: 'https://reqres.in/api/login',
            method: 'POST',
            send: credentials,
            category: 'login',
            ok: () => true, // I don't know how to deal with xstream errors, so pass everything
        }))

Upvotes: 0

Views: 94

Answers (1)

nlarche
nlarche

Reputation: 132

I don't think to well understand your use case but you can try something like this:

function main(sources: Sources): Sinks {
  // emits credentials whenever the the form is submitted
  const credentials$: Stream<Credentials> = sources.DOM.select(".form")
    .events("submit", { preventDefault: true })
    .debug("sad")
    .map((e: any) => ({
      username: "username", //"[email protected]",
      password: "password" //"cityslicka",
    }))
    .remember(); // remember the last value

  // filter failed responses
  const FailedResponse$: Stream<boolean> = sources.HTTP.select("login")
    .flatten()
    .filter(response => !response.ok);

  // maps emitted credentials onto an HTTP RequestOptions to be POSTed
  const request$: Stream<RequestOptions> = credentials$.map(credentials => ({
    url: "https://reqres.in/api/login",
    method: "POST",
    send: credentials,
    category: "login",
    ok: () => true // I don't know how to deal with xstream errors, so pass everything
  }));

 // map failed response to a new request
  const tryAgain$ = FailedResponse$.mapTo(request$)
    .flatten()
    .take(2); // here only take 2 to prevent infinite request

  return {
    HTTP: xs.merge(request$, tryAgain$),
    DOM: xs.of(form(".form", [input(".submit", { attrs: { type: "submit" } })]))
  };
}

I made a working exemple here : https://codesandbox.io/s/frosty-grass-1d5h8

I hope it's help you.

Upvotes: 1

Related Questions