-
-
Notifications
You must be signed in to change notification settings - Fork 97
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
Simplified typing #481
Comments
As part of this change the operators are also extremely simplified, and best of all... you can just compose any action directly in as an operator. const someAction = ({}: Context, value: string) => {}
const operator = pipe(
someAction,
wait(500)
) It basically means operators finally are interoperable with plain actions. Which also means we do not need This is such a huge improvement! |
Looks like a great improvement and it doesn't look like a lot of work to refactor our app. |
Looks good, will there be errors if the signature of actions doesn't match (Context, Payload). f.ex. what would happen if an action similar to this is defined:
|
@jmattheis Good question! Yes, when the actions are attached to Overmind it expects the signature of: ´(context: IContext)` , so you will get the error where you instantiate Overmind rather than where you define the action 😄 |
So a summary of what is being moved into New typing, no more implicit typing: import { IContext } from 'overmind'
const config = { ... }
export type Context = IContext<{
state: typeof config.state,
actions: typeof config.actions,
effects: typeof config.effects
}> Type actions: const myAction = (context: Context) => {}
const myActionWithPayload = (context: Context, payload: string) => {} Operator changes:
const whatever = pipe(
(context: Context, input: string) => input.toUpperCase()
)
pipe(
// "type" is a property on the object on the pipe
fork('type', {
FOO: (context: Context, event) => {
event // Is now typed as FooEvent
},
BAR: (context: context, event) => {
event // Is now typed as BarEvent
}
})
)
React hooks
const ItemComponent = ({ id }) => {
// The state you access within the callback is not tracked,
// only the result and further access is tracked. Meaning this
// component will only track the item, not changes to "items"
const item = useAppState(state => state.items[id])
} |
@christianalfoni can you give full example of the new typings feature with a simple state and action? because when I try it like this typescript complains:
|
Hi @ssijak , when you write this in the same file you will get this error. If you rather do: export type Context = IContext<{
state: typeof state,
actions: typeof actions,
effects: typeof effects
}> Then it works within the same file. |
Thanks for the quick answer! It works now, it does not complain. But I noticed some possible issues around actions. I am a relative typescript noob so maybe I'm talking nonsense but: If I type action parameters as any like :
than c.changeState bellow is inferred to
|
Thanks for testing! Doing
But this is good for documentation for sure! |
Thanks. I encountered one more possible issue. I do not find edit: So I should just use onInitializeOvermind action? |
@christianalfoni This is all coming in V28 right? Do you have a rough release date in mind? |
There has been a little bit lack of time lately (Blame the baby mostly 😉 ), but also at codesandbox.io we are hiring a few people and it has also taken up a lot of my time. But if you look at the issue list now I have marked some issues as So yeah, I hope to get some time soon to get through the pending issue and get that release out 😄 |
@christianalfoni I wanted to try out these new typings with my current namespaced config where my state and actions are placed in a separate folder. However in order to access the new
import { IContext } from 'overmind'
import { namespaced } from 'overmind/config'
import * as status from './status' //<---- cyclic dependency
export const config = namespaced({
status,
})
export type Context = IContext<{
state: typeof config.state
actions: typeof config.actions
}>
import { state } from './state'
import * as actions from './actions'
export { state, actions }
import { Context } from '..' // <---- cyclic dependency
export const updateState = (context: Context, payload: string) => {} Thanks for an awesome library! |
@mattiasjosefsson Move config into a separate file :-) |
@eirikhm Aaah of course! Thanks! |
@eirikhm Finally got around to test it but it doesn't help to move config into a separate file because |
@mattiasjosefsson something like this:
import { merge, namespaced } from "overmind/config"
import { state } from "./state"
import * as actions from "./actions"
import * as effects from "./effects"
import * as status from "./status"
export const config = merge(
{
state,
actions,
effects,
},
namespaced({
status,
})
)
import {
createActionsHook,
createStateHook,
createEffectsHook,
} from "overmind-react";
import { IContext } from "overmind";
import { config } from "./config";
export type Context = IContext<{
state: typeof config.state,
actions: typeof config.actions,
effects: typeof config.effects,
}>
export type RootState = typeof config.state;
export const useAppState = createStateHook<Context>()
export const useActions = createActionsHook<Context>()
export const useEffects = createEffectsHook<Context>()
import { Context } from "../index"
export const updateState = (context: Context, payload: string) => {} |
@eirikhm Could it be just eslint that complains when it's not really an issue? It will complain with the rule
import { Context } from '.' // Dependency cycle via ./config:4 eslint(import/no-cycle)
export const updateState = (context: Context, payload: string) => {}
import { state } from './state'
import * as actions from './actions' // Dependency cycle via .:1 eslint(import/no-cycle)
export const config = {
state,
actions,
}
import { createActionsHook, createStateHook } from 'overmind-react'
import { IContext } from 'overmind'
import { config } from './config' // Dependency cycle via ./actions:2 eslint(import/no-cycle)
export type Context = IContext<{
state: typeof config.state
actions: typeof config.actions
}>
export type RootState = typeof config.state
export const useAppState = createStateHook<Context>()
export const useActions = createActionsHook<Context>()
export type TestState = {
test: string
}
export const state: TestState = {
test: '',
} |
@christianalfoni So there is no way to simplify writing actions? Duplicating const actions = {
example (context: Context, params: { a: string, b: boolean }) {}
} Right now if I try to write this way I get the error:
|
@mattiasjosefsson no, it is not an issue with eslint. It is definitely a cyclic reference. I faced the same problem. It doesn't matter if some portion of code gets moved into a separate file, because What you could try to do is declaring the module to define the Context type
import type { IContext } from 'overmind';
import {
createActionsHook, createEffectsHook, createStateHook, createReactionHook
} from 'overmind-react';
import { merge, namespaced } from 'overmind/config';
import * as settings from './settings';
const overmindStoreConfig = merge(
{
state: {},
actions: {},
effects: {}
},
namespaced({
settings
})
);
type OvermindConfig = IContext<typeof overmindStoreConfig>;
// here
declare module 'overmind' {
interface Context extends OvermindConfig {}
}
const useOvermindActions = createActionsHook<OvermindConfig>();
const useOvermindState = createStateHook<OvermindConfig>();
const useOvermindEffects = createEffectsHook<OvermindConfig>();
const useOvermindReaction = createReactionHook<OvermindConfig>();
export {
useOvermindEffects,
useOvermindActions,
useOvermindState,
useOvermindReaction,
overmindStoreConfig
};
import type { IAction, Context } from 'overmind'; // and then import it like this
import type { State } from './state';
const updateSettings: IAction<State, void> = ({ state }: Context, settings) => {
state.settings = {
...settings
};
};
const onInitializeOvermind: IAction<Context, void> = ({ effects }, instance) => {
instance.reaction(
(state) => state.settings,
(settings) => effects.storage.save(settings),
{ nested: true }
);
};
export {
updateSettings,
onInitializeOvermind
}; |
Dear Overminders!
I have one thing I really want to do and that is to clean up and simplify the typing of Overmind. It was a long process figuring out how to do it and with acquired knowledge through all this work and later projects I would like to do this:
There will be no more implicit typing, the above is what everybody will need to do. This is how you type the different things:
And I believe that is it. This will need to be a breaking change to actually clean up the internal typing of Overmind. I was just wondering how you feel about this change, do you consider refactoring your apps to this typing too much work? 😄
The text was updated successfully, but these errors were encountered: