Repository URL to install this package:
|
Version:
1.2.9 ▾
|
/* 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