Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactions + DI & IoC proposal #38

Closed
belozer opened this issue Aug 29, 2019 · 3 comments · Fixed by #277
Closed

Reactions + DI & IoC proposal #38

belozer opened this issue Aug 29, 2019 · 3 comments · Fixed by #277
Assignees

Comments

@belozer
Copy link
Contributor

belozer commented Aug 29, 2019

Purposes

  1. simple mocks
  2. override between stores
  3. atomic
  4. safe use in stores / services Services (Async actions / Effects) #36 (comment) (use default value)
  5. Good type inference

Implementation

version 1

let id = 0;
export const declareEnv = (value) => {
  const key = ++id;
  return Object.assign(override => ({ key, value: override }), { key, value })
}

const Network = declareEnv(fetch)
const store = createStore(RootAtom, {}, [
  Network(myCustomFetch) // Override defalut value in this store
]);

version 2

it's harder to override inside the store.
getEnv(Network(cutomFetch)) vs getEnv(Network.Provide(cutomFetch))

let id = 0;
export const declareEnv = (value) => {
  const key = ++id;
  return { 
    Provide: override => ({ key, value: override }), 
    key, 
    value
  }
}

const Network = declareEnv(fetch)
const store = createStore(RootAtom, {}, [
  Network.Provide(myCustomFetch) // Override defalut value in this store
]);

Inside createStore

const _env = {};
for(const e of env) _env[e.key] = e.value

const getEnv = ({ key, value }) => _env[key] || value

Example usage

const MyService = ({ getEnv }: Store) => payload => {
  getEnv(Network)(payload)
};
@belozer belozer self-assigned this Aug 29, 2019
@belozer belozer changed the title Store env Store env (declareEnv) Aug 29, 2019
@artalar
Copy link
Owner

artalar commented Sep 6, 2019

/***** DI & IoC PROPOSAL *****/

// DIRTY HACK EXAMPLE

const fetchUserDone = declareAction()
const fetchUser = declareAction(
  null,
  (store, payload) => store.fetch('/user', payload)
    .then(response => store.dispatch(fetchUserDone(response)))
)

// implementation

const store = createStore()
store.fetch = process.env.TEST
  ? (...a) => Promise.resolve(__data__)
  : fetch

// BRANDED (TYPE) SAFETY CONTEXTS EXAMPLE

const fetchContext = declareContext()

const fetchUserDone = declareAction()
const fetchUser = declareAction(
  null,
  fetchContext.handle(
    (store, payload, fetch) => fetch('/user', payload)
      .then(response => store.dispatch(fetchUserDone(response)))
  )
)

const store = withContext(createStore(), [
  fetchContext.init(
    process.env.TEST ? (...a) => Promise.resolve(__data__) : fetch
  )
])

// implementation

function declareContext(
  createHandler = (store) => (cb, payload, ctx) => cb(store, payload, ctx)
) {
  const id = Symbol()
  return {
    init: context => ({ id, context, createHandler }),
    handle (cb) => (payload, { [id]: meta }) =>
      meta && meta.handler(cb, payload, meta.context),
  }
}

function withContext(store, contextWorkers) {
  contextWorkers.forEach(
    ({ id, context, createHandler }) => store[id] = { context, handler: createHandler(store) }
  )
  return store
}

// MAXIMUM SUGAR EXAMPLE

const [fetchUser, fetchUserDone] = declareFetch(payload => ['/user', payload])

// implementation

function declareFetch(cb) {
  const responseAction = declareAction('fetch response')
  const errorAction = declareAction('fetch error')
  const requestAction = declareAction(
    'fetch request',
    fetchContext.handle(
      (store, payload, fetch) => fetch(...cb(payload))
        .then(response => store.dispatch(responseAction(response)))
        .catch(error => store.dispatch(errorAction(error)))
    )
  )
  return [requestAction, responseAction, errorAction]
}

@artalar artalar changed the title Store env (declareEnv) DI & IoC proposal Sep 6, 2019
@artalar artalar changed the title DI & IoC proposal Reactions + DI & IoC proposal Sep 8, 2019
@Wroud
Copy link
Contributor

Wroud commented Sep 8, 2019

const fetchFactory = createFactory();
// declare function createFactory<T>(): <U extends any[], V>(...args: U) => (f: (x: T) => (...args: U) => V) => V;
const helpFactory = createFactory();

store
    .attach(fetchFactory, fetch)
    .attach(helpFactory, {
        url: {
            myRequest: id => `user/${id}`
        }
    });

// or we can use something independent of reatom
const fetchFactory = createFactory('domain'); 
const helpFactory = createFactory('domain');

attachFactory('domain', fetchFactory, fetch)
attachFactory('domain', helpFactory, {
    url: {
        myRequest: id => `user/${id}`
    }
});
//

const responseAction = declareAction('fetch response')
const errorAction = declareAction('fetch error')
const requestAction = declareAction(
    'fetch request',
    fetchFactory(fetch =>
        helpFactory(({ url }) =>
            store => payload => fetch(url.myRequest(payload))
                .then(response => store.dispatch(responseAction(response)))
                .catch(error => store.dispatch(errorAction(error)))
        )
    )
);

@belozer
Copy link
Contributor Author

belozer commented Sep 8, 2019

Context

implementation

let id = 0;
export const declareContext = (value) => {
  const key = ++id;
  return { 
    Provide: override => ({ key, value: override }), 
    key, 
    value
  }
}

usage

const Network = declareContext(fetch);

override if need pointwise (not initialize!)

const store = createStore(RootAtom, {}, [
  Network.Provide(fetchForTests) // Override defalut context value in this store
]);

const store = createStore(RootAtom, {}); // Network context should works inside store

// OR as hoc like
// function withContext(store, contextOverrides) {}

getContext

implementation inside store

// fill data
const _contexts = {};
for(const ctx of contexts) _contexts[ctx.key] = e.value

const getContext = ({ key, value }) => _contexts[key] || value

Reactions

usage

const fetchPage = declareAction(async (store, payload) => {
  // Data from another atom
  const sort = store.getState(Sort); 

  // Use declarated fetch
  const res = await store.getContext(Network).get(`/products/${payload}`, { sort });
  // Use async normailzer
  const data = await store.getContext(Normalizer).normalize(res.data, schema);

  // Push data to store
  store.dispatch(fetchPageDone(data.result));
})
const fetchPageDone = declareAction()

const ProductsPage = declareAtom({ 
  products: [], 
  page: 1, 
  loading: false 
}, on => [
  on(fetchPage, (state, payload) => { ...state, loading: true, page: payload }),
  on(fetchPageDone, (state, payload) => { ..state, loading: false, products: payload }),
])

@artalar artalar added docs and removed enhancement labels Oct 16, 2019
@belozer belozer removed the docs label Oct 26, 2019
artalar added a commit that referenced this issue Dec 21, 2019
artalar added a commit that referenced this issue Dec 22, 2019
artalar added a commit that referenced this issue Dec 22, 2019
artalar added a commit that referenced this issue Dec 23, 2019
artalar added a commit that referenced this issue Jan 16, 2020
artalar added a commit that referenced this issue Jan 16, 2020
@artalar artalar mentioned this issue Jan 16, 2020
artalar added a commit that referenced this issue Jan 16, 2020
artalar added a commit that referenced this issue Jan 16, 2020
artalar added a commit that referenced this issue Jan 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants