Code Monkey home page Code Monkey logo

Comments (8)

jeffbski avatar jeffbski commented on June 27, 2024 2

I'll write up an RFC for this addition and see what everyone thinks.

from redux-logic.

jeffbski avatar jeffbski commented on June 27, 2024 1

I have added logicMiddleware.addDeps(additionalDeps) which will allow you to add dependencies after createStore has been called (similar to the example I provided). It is now available as of v0.11.7

https://github.com/jeffbski/redux-logic/releases/tag/v0.11.7

I'll go ahead and close this, but if there are any additional questions just let me know.

from redux-logic.

erkiesken avatar erkiesken commented on June 27, 2024 1

We've been using this approach for setting API credentials:

When a token is fetched from somewhere, it is stored in state by sending save-api-token action (ends up saved in some reducer). Then there's also a logic that listens for this save-api-token action and gets api dependency and calls api.setAuthToken(action.payload.token) on it as a side-effect (api instance now saves it internally as this._token). Now every other logic just needs to use api dependency and call any api.doSomethingResty calls without having to know about tokens at all.

from redux-logic.

jeffbski avatar jeffbski commented on June 27, 2024

@michael-wolfenden thanks for the kind feedback and for submitting this issue.

I believe I understand what you need here. I want to be careful to allow people to use deps to pass in any sort of object, function, or primitive, so we'd have to keep that in mind with any additions. To go down that path, it seems like we'd need a special way to signify that this is a fn that receives state if redux-logic is supposed to automatically provide it and thus the API becomes a little more complex.

So I'm assuming that you would have something like this currently.

const api = {
  foo(accessToken, id) // returns a promise
}
const logicMiddleware = createLogicMiddleware(logic, { api });

const fooLogic = createLogic({
  type: FOO,
  process({ getState, action, api }, dispatch, done) {
    const accessToken = getState().accessToken;
    const id = action.payload;
    api.foo(accessToken, id)
      .then(result => dispatch({ type: FOO_SUCCESS, payload: result }))
      .then(() => done());
  }
});

Personally this is how I like to craft my logic so that all of the redux related stuff is encapsulated in the logic keeping my API clean and separate from anything redux, so it can be used in other ways even without redux. So for me all the mapping to and from actions, state, selectors, dispatching, all of that occurs inside the logic so that everything else can stay pure. It is also a good practice to use selectors to find things in the state so you can easily restructure the state as necessary, so in the above example

const accessToken = accessTokenSelector(getState()); // in fooLogic
// and the selector would have been defined in the same file with the reducer something like
export const accessTokenSelector = state => state.accessToken;

So for me this is ideal having the api.foo be passed in accessToken and id, the two things it needs to do its work. It is easy to test in isolation, I don't have to set up a full state when I only need the accessToken.

However in thinking through your desire to not need to always pass this accessToken through for every call. I'm guessing that your desired code would look like this.

const fooLogic = createLogic({
  type: FOO,
  process({ getState, action, api }, dispatch, done) {
    const id = action.payload;
    api.foo(id) // only need to pass in id, since foo already has getState
      .then(result => dispatch({ type: FOO_SUCCESS, payload: result }))
      .then(() => done());
  }
});

Basically eliminating the need to retrieve and pass the accessToken inside the logic. So while I would still prefer the previous form, I can see how some might prefer less repetition (though we have basically shifted the selector call from the logic to inside the api call).

I had been already pondering the ability to add dependencies at runtime but I wanted to be careful on how this is done.

So I am wondering if that mechanism could be used to help you with your goal as well.

Here's how your code might look if we added the addDeps method to redux-logic.

function createAPI(getState) { 
  // we basically capture getState in our closure so it doesn't have
  // to be passed in later
  return { // api
    foo(id) {
      const accessToken = accessTokenSelector(getState());
      // return promise
    }
  }
}
const logicMiddleware = createLogicMiddleware(logic);
const store = createStore(reducer, applyMiddleware(logicMiddleware));
logicMiddleware.addDeps({
  api: createAPI(store.getState)
});

const fooLogic = createLogic({
  type: FOO,
  process({ getState, action, api }, dispatch, done) {
    const id = action.payload;
    api.foo(id)
      .then(result => dispatch({ type: FOO_SUCCESS, payload: result }))
      .then(() => done());
  }
});

If we added something like addDeps does that move your code in the desired direction?

from redux-logic.

michael-wolfenden avatar michael-wolfenden commented on June 27, 2024

Thank @jeffbski for your response

Your correct in regards to how we are currently using it. We are also using a selector to access the token.

In terms of addDeps rather than closing over store.getState, I was thinking more in terms of

const deps = {
  api: (state) => createAPI(state)
};

const logicMiddleware = createLogicMiddleware(arrLogic, deps);

So the api passed into proccess is the result of executing this function with the current state.

Having said that either way will solve my issue.

from redux-logic.

jeffbski avatar jeffbski commented on June 27, 2024

Yeah, I thought that's what you were getting at. My main reason for suggesting the alternative is that otherwise we'd need a way to indicate that a dependency needed to be called with state first before providing to the hooks. We can't just take the fact that it is a function since we could have lots of other deps that are functions (which are not expecting to be called prior to injection). So we'd likely have to use a special key or wrap in an object deps that need pre-execution with state. It gets even worse if we provide state rather than getState since then we have to execute this for each an every one of these before each and every hook in each and every logic even if they don't use them.

Thus we will bypass a bunch of API complexity and extra work if we go with the addDeps approach.

What should we call these injected dependencies? Is deps clear enough? or is something like locals better? Originally I would have used context, but since React has context and we also have a pre-created variable name ctx (for sharing between hooks), so I went with the term deps.

Since I'm adding a new API method, I guess I better see if this is the best term. We can always change variable names around later, but API method names are more rigid.

Any thoughts on the best term for these injected dependencies?

from redux-logic.

michael-wolfenden avatar michael-wolfenden commented on June 27, 2024

Hmmm, I see what you mean.

How about stateDeps instead of deps?

from redux-logic.

jeffbski avatar jeffbski commented on June 27, 2024

@tehnomaag That sounds like a nice approach, thanks for sharing.

from redux-logic.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.