Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/framework / src / __todo / connectToData.tsx
Size: Mime:
/* eslint-disable max-statements */
/* eslint-disable brace-style */
/**
 * @todo option connectToData({shouldUpdate: false})
 * @todo ^ automagically with context
 *
 *
 * @fileoverview an example using mobx for easy fast pub-sub/observable stores with lots of comments
 *
 * @see https://mobx.js.org/intro/concepts.html
 * @tutorial https://mobx.js.org/getting-started.html
 * @description related
 * @tutorial https://reactjs.org/docs/hello-world.html
 */
import React from 'react'
/**
 * mobx
 * @description @tip use "[command] + [left-mouse-click]" to open up the type definitions!
 */
import { observable, computed, action, extendObservable } from 'xmobx/mobx'
import { observer, inject } from 'xmobx/mobx-react'
/**
 * utils
 * https://github.com/mobxjs/mobx-react/blob/master/src/inject.js#L36
 */
import { connectToData } from '@skava/modules/state-tree'
/**
 * component name debugs
 */
import { toComponentName } from '@skava/modules/identifier'
/**
 * every type check and type coersions
 */
import { isFunction, isObj } from '@skava/modules/exotic'
/**
 * container for building containers, 3d printer for your 3d printer
 */
// import BaseChain from 'modules/chain-able/container-builder'
/**
 * our main factory connector, which later will be using this one, and this one becomes a chain
 */
import { tapProps, decorateComponentStatics } from './deps'

/**
 * ===============================================
 * reusable utils, this would always be in modules
 * @see modules/state-tree
 * @see modules/view-container
 * ===============================================
 */
const IS_BROWSER = typeof window === 'object'

/**
 * @alias AddStoreToPropsDecorator
 * @tutorial https://reactjs.org/docs/higher-order-components.html
 * @type {Function.Decorator}
 *
 * @param {ObservableContainer} container
 * @param {React.Component} TargetViewComponent
 * @return {React.Component} obserable/subscribed component connected to ^ store
 */
function withStore(container, TargetViewComponent) {
  /**
   * @curried 1
   * @desc return function if only 1 arg is passed in
   *
   * @param {React.Component} Target
   * @return {withStore}
   */
  if (arguments.length === 1) {
    return function curriedConnectToData(Target) {
      return withStore(container, Target)
    }
  }

  const remapInject = contextStores => {
    return {
      store: contextStores.store,
      ...contextStores,
    }
  }

  /**
   * make our component observe every change to the store
   * the "subscribe" in pub-sub
   * @type {React.Component}
   * @see https://mobx.js.org/refguide/observer-component.html
   */
  const ObservableView = observer(TargetViewComponent)
  // const ViewComponentSubscribedToStore = obserable(TargetViewComponent)
  const ViewComponentSubscribedToStore = inject(remapInject)(ObservableView)

  /**
   * @func @observer
   * @type {Function.Decorator}
   * @see http://bit.ly/typescript-decorators
   * @see http://bit.ly/babel-decorators-transpiled
   * @tutorial https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841
   * @tutorial https://mobx.js.org/refguide/observable.html
   * @see https://github.com/mobxjs/mobx/blob/master/src/core/observable.ts
   * @see https://github.com/mobxjs/mobx/blob/master/src/api/observabledecorator.ts
   *
   * we want to make sure this wrapper is updated every time the props change
   * so that it will more easily pass the data
   * down to the TargetViewComponent that it's wrapping
   */

  const named = container.named || container.name || container

  /**
   * @todo add dynamic injector from param here
   *
   * @alias HigherOrderComponentSubscribedToStore
   * @class HigherOrderComponentWrapper
   * @tutorial https://medium.com/@learnreact/container-components-c0e67432e005
   */
  // @inject('store')
  @inject(remapInject)
  @observer
  class ViewInsideContainer extends React.Component {
    // For server-side rendering (see getDataFromTree.ts)
    // @todo - could synchronize the state observably to container, if needed
    // state = {}
    // eslint-disable-next-line
    async fetchData() {
      // let state
      // throw new Error('_SERVER_SIDE_RENDERING_DO_NOT_USE_ME_OR_ELSE_DOT_DOT_DOT')
      // container.dynamicState.queryResponse
      if (isFunction(container.fetchInitialOnServer)) {
        const response = await container.fetchInitialOnServer()
        // _DO_NOT_USE_ME_OR_ELSE_DOT_DOT_DOT
        console.dev('_SERVER_SIDE_RENDERING')
        // @todo - this has errors in updating and re-updating
        // this.setState(response)
        return response
      } else {
        // _SERVER_SIDE_RENDERING_DO_NOT_USE_ME_OR_ELSE_DOT_DOT_DOT
        console.warn('_NO_FETCHINITIALONSERVER')
        // TargetViewComponent.displayName || TargetViewComponent
        console.dev(named, toComponentName(TargetViewComponent))
        console.dev('\n\n\n')
        // state = {}
      }
      // console.dev({ state })
      return Promise.resolve({ isEmpty: true, name: container.name })
    }
    // eslint-disable-next-line
    async asyncBootstrap() {
      return this.fetchData()
    }
    async bootstrap() {
      return this.fetchData()
    }

    /**
     * @description !!! CALLED ON [SERVER + WEB]
     *
     * @tutorial https://reactjs.org/docs/state-and-lifecycle.html
     * @see http://busypeoples.github.io/post/react-component-lifecycle/
     * @param {React.Props} nextProps
     * @listens componentWillReceiveProps
     * @fires fetchInitialOnServer if route changes
     */
    componentWillMount() {
      /**
       * @todo because withJobs rehydrates here
       *
       * @todo @fixme something with header uses this, and it FOUC it - fix that & remove this
       */
      if (
        this.props.jobResult &&
        typeof window === 'object' &&
        isFunction(container.rehydrateInitialOnWeb)
      ) {
        container.rehydrateInitialOnWeb(this.props.jobResult || this.props)
        //   if (isFunction(container.handleMount)) {
        //     container.handleMount(this.props.jobResult, this)
        //   }
      }
      if (isFunction(container.handleMount)) {
        // console.dev('componentWillMountcomponentWillMountcomponentWillMountcomponentWillMountcomponentWillMount')
        container.handleMount(this.props, this)
      } else {
        console.dev(named, '_____NO_WILL_MOUNT_____')
      }
      // isServer
      if (IS_BROWSER === false) {
        // @see didMount
        // container.fetchInitialOnServer(this)
        // container.rehydrateInitialOnWeb(this)
        // container.handleDidMount(this.props, this)
      }

      // console.dev('All The Things', this.props, this)
    }

    /**
     * @description !!! THIS IS ONLY ON [BROWSER/CLIENT]
     * ^ happens before rendering
     *
     * @tutorial https://github.com/ctrlplusb/react-jobs
     * @see wantsServerSideRenderDataOnly
     * @see container.fetchInitialOnServer
     * @listens componentWillMount
     * @alias onInitialCreate
     * @fires fetchInitialOnServer
     * @fires handleMount
     */
    componentDidMount() {
      // @todo
      const props = this.props.jobResult
      // @note with props && we only call rehydrate when we are in fact rehydrating
      // props &&
      if (isFunction(container.rehydrateInitialOnWeb) === true) {
        // @todoooooooo
        // const props = this.props.jobResult
        // return container.rehydrateInitialOnWeb(props || this.props, this)
        return container.rehydrateInitialOnWeb(props, this)
      } else {
        // console.dev('componentDidMountcomponentDidMountcomponentDidMountcomponentDidMountcomponentDidMountcomponentDidMountcomponentDidMount')
        return undefined
      }
      // if (isFunction(container.handleDidMount) === true) {
      //   // @todoooooooo
      //   container.handleDidMount(this.props, this)
      // }
    }

    // /**
    //  * @see handleProps
    //  * @see componentWillReceiveProps
    //  */
    // handleUpdate(props) {
    //   //
    // }

    /**
     * @todo getDerivedStateFromProps
     * @api https://github.com/reactjs/rfcs/blob/master/text/0006-static-lifecycle-methods.md#basic-example
     * @see https://twitter.com/dan_abramov/status/953612246634188800?lang=en
     */
    // static getDerivedStateFromProps(nextProps, prevState) {
    //   // Called after a component is instantiated or before it receives new props.
    //   // Return an object to update state in response to prop changes.
    //   // Return null to indicate no change to state.
    // }

    /**
     * @deprecated
     */
    componentWillReceiveProps(nextProps) {
      /**
       * @description this is to check whether the hydrated data has changed based on the route
       * @todo I will inline react-jobs to remove external dependency api
       */
      container.handleUpdate(nextProps)

      if (
        isObj(nextProps.jobResult) &&
        nextProps.jobResult.identifier !== this.props.jobResult.identifier
      ) {
        this.componentWillMount()
      }

      if (
        isObj(nextProps.match) &&
        // nextProps.location.key !== this.props.location.key
        nextProps.match.params !== this.props.match.params
      ) {
        console.dev({
          previousLocation: this.props.location,
          currentLocation: nextProps.location,
        })
        this.componentWillMount()
      }
    }

    /**
     * @event componentWillMount
     * | is executed before rendering,
     * | on both the server and the client side.
     *
     * @event componentDidMount
     * | is executed after the first render only on the client side.
     * | This is where AJAX requests and DOM or state updates should occur.
     * | This method is also used for integration
     * | with other JavaScript frameworks and any functions
     * | with delayed execution such as setTimeout or setInterval.
     * | We are using it to update the state
     * | so we can trigger the other lifecycle methods.
     *
     * @event componentWillReceiveProps
     * | is invoked as soon as the props are updated before another render is called.
     * | We triggered it from setNewNumber when we updated the state.
     *
     * @event shouldComponentUpdate
     * | should return true or false value.
     * | This will determine if the component will be updated or not.
     * | This is set to true by default.
     * | If you are sure that the component doesn't need to render
     * | after state or props are updated, you can return false value.
     * | (this is what PureComponent auto helps with)
     *
     * @event componentWillUpdate
     * | is called just before rendering.
     *
     * @event componentDidUpdate
     * | is called just after rendering.
     *
     * @event componentWillUnmount
     * | is called after the component is unmounted from the dom.
     * | We are unmounting our component in main.js
     *
     * @listens render
     * @fires handleProps
     * @return {React.ClassicComponent}
     */
    render() {
      const props =
        container.isContainer || container.handleProps
          ? container._handleProps(this.props, this)
          : tapProps(this.props)

      /**
       * @tutorial https://reactjs.org/docs/react-without-jsx.html
       * @example http://bit.ly/babel-react-create-component
       *
       * this is the same as
       * @extends React.createComponent(ViewComponentSubscribedToStore, props)
       */
      return <ViewComponentSubscribedToStore {...props} />
    }
  }

  decorateComponentStatics(ViewInsideContainer, TargetViewComponent, container)
  // displayName=💉
  ViewInsideContainer.displayName = 'connectToData(' + toComponentName(TargetViewComponent) + ')'

  /**
   * @description if we have ssr, use it, otherwise, done
   * && container.fetchInitialOnServer.length !== 5
   */
  if (isFunction(container.fetchInitialOnServer)) {
    /**
     * @see ./oneServerSideRender
     * @note - removed react-jobs anyway
     */
    // const withServerSideRendering = oneServerSideRender(
    //   container.fetchInitialOnServer
    // )
    // ViewInsideContainer.getData = container.fetchInitialOnServer
  } else {
    return ViewInsideContainer
  }
}

export { connectToData, withStore }
export default connectToData