farrow-next
farrow-next
is an upper-level business framework based on the next.js
package, containing
A conceptual architecture based on the classic
MVC
A
redux-based
state managementBased on the model of
Inverse of Control
andDependency Injection
to manage business codeProvides a friendly
React-Hooks api
Wraps
cookie|fetch|userAgent
and other convenient isomorphic methodsBased on
TypeScript
development, providing good type derivation capabilities...
#
Installation#
Contents#
Basic UsageEach page consists of 3 parts: Model
, View
, Controller
.
Model
manages the state of the application and its changesView
manages the interface of the application and its event bindingController
manages the asynchronous interaction of the application (e.g. requesting data)
In farrow-next
, the Model
is inside the Controller
and each Controller
has its own Model
that it maintains.
Each Model
consists of state
and reducers/actions
, which can be understood as a redux-store
.
There is only one View
, but there can be multiple Controllers
.
The View
accesses the state, actions
and other properties or methods inside each Controller
through the hooks api
.
#
Step 1: Define the ControllerController
can be defined as many times as needed.
#
Step 2: Defining ViewA View
is a React Component
, and in any function component, you can:
Get an instance of
Controller
withController.use()
.Pull state data from
Controller
viaController.useState(selector?)
, and automatically update the view when the state changes
#
Step 3: Create the Page componentAfter completing the two steps, we need to bind them together to create a page.
#
Step 4: Expose the Page componentIn pages/xxx.ts
, expose the Page
component to be accessible via url.
#
API#
React Hooks API#
Controller.use()Get the Controller
instance in the React Function Component
#
Controller.useState(selector?, compare?)Get and listen to Controller
's state
in React Function Component
- selector (optional) with
state
as argument returns the result of the state selected from it, default is state => state - compare (optional), with
(currState, prevState)
, returnstrue
if the component needs to be re-rendered, orfalse
if it is not. The default isshallowEqual
which is shallow compared two object.
#
usePageInfo(): PageInfousePageInfo
to access current page info
#
useQueryChangedEffect(effectCallback)useQueryChangedEffect
to perform effect when query was changed
#
Controller API#
controller.initialStateInitial state of the Controller
to initialize the redux store
#
controller.reducersThe reducers
object of a Controller
contains the reducer
function to update the state
.
reducers
is an object { [key: string]: Reducer }
whose key
is its action-type
.
#
controller.storeAccess the redux-store
constructed from initialState/reducers
#
controller.stateAccesses the current this.store.getState()
latest state
#
controller.actionsAccesses the actions
update function of redux-store
, with the same structure as this.reducers
.
#
controller.pageAccess the data associated with NextPageContext
, structured roughly as follows
#
controller.devtoolsWhether to enable redux-devtools
, default is true
.
Supports boolean | string
, if it is string
, it will be displayed as the name in redux-devtools
, which can be displayed normally even after compressing the code (the default name is the class name of Controller.name
, which becomes a single letter after compressing.
#
controller.loggerWhether to enable redux-logger
, default is false
.
#
controller.fetch(url:string, options?: RequestInit)fetch
method wrapper, automatically handles cookie
passing internally, interface is consistent with global variable fetch
See fetch
documentation for more information.
#
controller.getJson(url:string, params?:object, options?: RequestInit): jsonThe controller.getJson method is a method based on the controller.fetch wrapper to make it easier to send get requests.
The url parameters are handled in the same way as the controller.fetch method.
The params parameter will be internally querystring.stringify and spliced after the url.
The options parameter will be passed as options for the fetch.
#
controller.postJson(url:string, body?:object, options?:RequestInit): jsonThe controller.postJson method is based on the controller.fetch wrapper method, and is a simpler way to send post requests.
The url parameter is handled in the same way as the controller.fetch method.
If the data is an object, it will be internally JSON.stringify and then sent to the server as a request payload
The options parameter will be passed as options for the fetch.
#
controller.getCookie(key:string)controller.getCookie is used to get the value of the cookie corresponding to the key parameter.
#
controller.setCookie(key:string, value:string, options?:object)controller.setCookie is used to set the value of the cookie corresponding to the key parameter. The third parameter options is an object, see documentation
#
controller.removeCookie(key:string, options?:object)controller.removeCookie is used to remove the value of the cookie corresponding to the key parameter. The third parameter options is an object, see documentation
#
controller.redirect(url)controller.redirect
is used to redirect, it will take care the server/client
, and chose the right way to redirect.
#
controller.isClientcontroller.isClient is a boolean value that determines whether the client is currently on the server.
#
controller.isServercontroller.isServer is the boolean value that determines whether the client is currently on the server side.
#
controller.userAgentGets the userAgent
string, which can be used to construct other properties or methods such as controller.isWeixin
.
#
controller.use(Controller)The controller.use
method is used to implement dependency injection and returns the instance of the used class.
See Dependency Injection for more on this.
#
Controller Life-Cyclecontroller.preload?(): Promise<void>
#
Call on preload phase(before component rendering), you can fetch SSR
related data in this method
#
Page Api#
Dependency InjectionThe Controller class
implements dependency injection, meaning that within a Controller
, instances of other controllers can be injected via this.use(Controller)
, and can even refer to each other.
This mechanism facilitates modularity by giving preference to combinations over inheritance.