frankmeza
7/20/2018 - 10:07 PM

monorepo docs

monorepo docs

Table of Contents

  1. UI Components, Core Functions, Redux Actions
  2. Actions, Reducers, Saga Functions

1 :: UI Components, Core Functions, Redux Actions

  1. UI -> UI: actual user interaction calls a component function
  2. UI -> core: the component function calls a core function with optional arguments
  3. core -> actions: core functions create redux actions, passing in a payload
  4. actions -> reducers and/or redux sagas: actions are emitted to redux

1 :: Code Examples

UI -> UI
UI -> core

// 1. UI -> UI: actual user interaction calls a component func  
// 2. UI -> core: the component function calls core function with opt args

// make this a project specific living document: 
// where does each piece of code live?

// example function in *.tsx
onClick(param: string): void {
    // this is an action creator
    const { funcName, valueFromProps } = this.props
    funcName(param, valueFromProps)
}

// function is possibly passed into child component
render(): JSX.Element {
    <Button text="Confirm" onClick={this.onClick} />
}

### summary : actual user interaction, UI component functions, core functions

These are code examples of the first two messages in the flow of data: the calling of a UI function per the actual user, and then the calling of a core function inside of that UI function.

Both the above onClick and render functions will be found inside of a React.Component. Functions and other passed in values come into the component via React props, i.e. funcName. They're then destructured and may take in possible arguments from the component function.

It is common to see a React component pass its own functions into any child components within it, in this case the confirm button receives as props its parent's function, as this.onClick.

back to top of these code examples

back to top of section


core -> actions

// 3. core -> actions: core funcs create redux actions, passing in a payload
// make this part a living document: where does each piece of code live?

// example function type without params, with interface
interface ClearDataAction {
    readonly type: "CLEAR_DATA"
}

const clearData = () => ({
    type: "CLEAR_DATA",
})

// example function type with params, with interface
interface SetDataAction {
    readonly type: "SET_DATA"
    readonly attr1: string
    readonly attr2: string
    readonly attr3: number
    readonly attr4: boolean
}

const setData = (
    attr1: string,
    attr2: string,
    attr3: number,
    attr4: boolean,
): SetDataAction => ({
    type: "SET_DATA",
    attr1,
    attr2,
    attr3,
    attr4,
})

// union type for export
type GroupedDataActions =
    | SetDataAction
    | ClearDataAction

export { 
    // interface types
    GroupedDataActions,
    ClearDataAction,
    SetDataAction,

    // action creators
    clearData,
    setData,
}

### summary : core functions, redux actions

These are a few code examples of the third message in the flow of data: the core function inside of that UI function (in steps one and two above) creates a redux action.

A redux action returns an object with a type property, and may have other possible fields (its payload). Examples of this from above are clearData and setData. You can also each has their interface shape above it. The interfaces may be grouped as they are in GroupedDataActions, and then everything is exported.

back to section sequence diagram
back to top of code examples


actions, reducers, sagas

// 4. actions -> reducers and/or redux sagas: actions are emitted to redux

// These actions' types are listened for:
// in the reducers themselves,
// in the sagas, via the sagas/index.

// often, at end of saga function, new data is passed into reducer.

### summary : actions, reducers, redux sagas

Redux actions are listened for in both redux reducers and redux sagas by the action type and then the payload is used to mutate state. More details will be found in the following section.


2 :: Actions, Reducers, Saga Functions

  1. note: reducers and sagas both listen for an action's type, then take in its payload to perform state mutations, as mentioned at the end of the previous section.
  2. actions -> reducers: actions go into reducers as replacements to state, without logic
  3. actions -> sagas: complicated or state-ful logic is orchestrated in the sagas, where control flow lives

2 :: Code Examples

// 1. actions -> reducers: actions go into reducers for simple replacements to state, without logic  
// make this part a living document: where does each piece of code live?
import { DataShape, INITIAL_STATE } from "../core"

function plainReducer(action: ActionTypeInterface) {
    switch (action.type) {
        // this is the type listened for
        case "SET_DATA":
            // this is what is returned to the state tree,
            // only simple replacements
            return { ...state, data: action.data }
        case "CLEAR_DATA":
            return INITIAL_STATE
        default:
            return INITIAL_STATE
    }
}

// root reducer
import { rootReducer } from "redux"
import { plainReducer } from "../data/plainFile"
import { anotherPlainReducer } from "../data/anotherPlainFile"

// each reducer is fed into the rootReducer
const rootReducer = combineReducers({
    plainReducer,
    anotherPlainReducer,
})

// the rootReducer is then fed into the redux store...

// store.ts

import { createStore, applyMiddleware } from "redux"
import { Sagas, Reducers, Core } from "common"
import createSagaMiddleware from "redux-saga"
import { createLogger } from "redux-logger"

import { AppState } from "../core/app_state"

const logger = createLogger({
    stateTransformer: (state: AppState) => state,
})

// create saga middeware
const middleware = [
    createSagaMiddleware(),
    // other custom middleware here,
    logger,
]

// Add the reducer to your store on the `router` key
// Also apply our middleware for navigating
export const store = createStore(
    Reducers.rootReducer,
    applyMiddleware(...middleware),
)

sagaMiddleware.run(Sagas.root)

// sagas/index

import { funcName } from "../modules/shape_name/shape_sagas"
import { 
    funcName2,
    funcName3,
    funcName3,
} from "../modules/other_shape_name/other_shape_sagas"

const sagasIndex() {
    forEvery("ACTION_NAME1", funcName),
    forEvery("ACTION_NAME2", funcName2),
    // and more of the same
}

Redux Sagas

sagas -> external API: request is made for data to be exchanged
external API -> sagas: returns raw response
sagas -> utils: utils are called, to actually perform business logic
utils -> sagas: data is returned, fit for saga to give to an action
sagas -> actions: return final data to actions

Store, UI Components, UI As A Whole

Title: Store, UI Components, UI As  whole

store -> components: components are as presentational as possible
components -> ui: the components make up the ui, also as presentational as possible, maintaining miimal ui state
ui -> ui utils: sorting and filtering based on actual user interaction is handled in ui utils
// client.ts or web/api.ts


// sagas/index.ts

import { takeEvery } from "redux-saga/effects"
import { handleMetaData } from "../metadata_sagas"
import { handleClientMsg } from "../client_sagas"
import { handleLogin } from "../login_sagas"
import {
    handleLogout,
    handleLogoutResponse,
} from "../modules/logout/logout_sagas"

export default function* root(): {} {
    yield [
        takeEvery("HANDLE_LOGIN_RESP", handleLogin),
        takeEvery("LOGOUT", handleLogout),
        takeEvery("HANDLE_LOGOUT_RESPONSE", handleLogoutResponse),
        takeEvery("HANDLE_METADATA", handleMetaData),
        takeEvery("HANDLE_CLIENT_MSG", handleClientMsg),
    ]
}