@magic

@magic/core concepts

magic concepts. these are the building blocks of every module in a magic app

@magic-modules

modules are the main building block of magic.

a page is a module, a button is a module, a link is a module, an image is a module. even a theme is a module.

a @magic app contains modules containing modules that contain modules. this can lead to inception.

module building blocks

state

state is a javascript object. state can be mutated by actions, effects and subscriptions can call actions to change the state. every rendering frame, the state determines the output of the html views.

const state = {  shown: false,  count: 0,}

actions

actions are an object containing functions

those functions get the state and their props and may return a new full, but changed, state.

const actions = {  toggleModal: state => {    return {      ...state,      shown: !state.shown,    }  },  calculator: {    plus: state => ({ ...state, count: state.count + 1 }),    minus: state => ({ ...state, count: state.count - 1 }),  },}

effects

effects are an object containing functions, just like actions.

effects can only change the state by calling actions.

effects may also be impure and trigger side-effects outside of hyperapp.

const effects = {  waitASec: [state, async state => {    await new Promise((r) => setTimeout(r, 1000))    return actions.plus(state)  }],}

subscriptions

at the moment, subscriptions can be defined using strings as function names.

also, if a module exports a subscriptions array, those subscriptions will be added to the app.

// in a module:export const lib = {  subscribe: (dispatch, action) => {    setInterval(() => {      dispatch(action, { arg: Math.random() * 100 })    })  },}export const actions = {  onSubscription: (state, e) => ({    ...state,    arg: e.arg,  })}export const subscriptions = ['lib.subscribe', 'actions.onSubscription']

views

views render the state to html whenever an action triggers a change in the state, this then triggers a view change.

export const View = state => div(JSON.stringify(state))

styles

every module can export a style object. magic will automagically merge all styles into one global css file. in the future, it will also remove unused styles for you.

styles merge order, last overwrites first: module.style < page.style < app.style < theme.style

this allows css to be inherited in both directions.

a module automatically creates it's own css namespace, that same namespace can be used in page, app and theme css styles to overwrite module specific styles.

@magic/css

internally, magic uses it's own css-in-js library.

to find out more, click the following link: @magic/css

globals

every module can set a global object, containing state and action properties.

every state and/or action name in the global object with a value that equals true gets merged into the main app state/actions, instead of into the module namespace.

// in module.mjsexport const state = {  internal: 0,  external: 0,}export const actions = {  internal: state => ({    ...state,    module: {      internal: state.module.internal + 1,    },  }),  external: state => ({    ...state,    external: state.external + 1,  }),}export const global = {  state: {    external: true  },  actions: {    external: true,  },}// in the view, we can use both the global and local actions and state variablesexport const View = state => div([  'view',  p({ onclick: actions.module.internal }, ['internal:', state.module.internal]),  p({ onclick: actions.external }, ['external:', state.external]),])

server lambdas

this is the serverside magic. you can define functions that get transpiled into serverside lambdas. server side lambdas will be available for GET and/or POST requests.

the server side function signature is (req, res) => {}, as it is in most nodejs http servers, with the addition of req.body being async => awaited before execution of the lambda.