web-app-tools
Web app packages, all in 1 without bloating.
Yet another customized react-scripts
, with minimal dependencies and customizations for building web apps.
Why
After ejecting from create-react-app
, we gain full control of everything, and we must manage all of them. There's no way back.
Instead of no configuration, we write minimal configurations only for customized parts.
Features
- React, Redux, Sass
- One Dependency: Essential elements to build web applications are provided.
- Good Defaults: Start with almost no configurations.
- Smart Configuration: Customize without entire configurations, write configurations only for needed parts and customize easily.
- Keep updated: since there's no ejection, we can simply upgrade this package.
- Tiny Bundle Size: Dependencies are carefully chosen to avoid bloating, tree shakable packages are used if possible.
- Offline Ready: Assets are cached for offline use and
manifest.json
is generated for adding to the home screen.
Getting Started
Make sure yarn is installed, it's required to enable PnP at this time.
PnP is ⚡lighting fast⚡, you should try it!
Create the project directory:
mkdir my-web-appcd my-web-appyarn init -yyarn add -D web-app-tools
And these files:
package.json
Use webpackConfig
to generate webpack options:
src/webpack.config.js
const webpackConfig = moduleexports =
Top-level component:
src/app.jsx
Entry file, start the app with HMR enabled:
src/index.js
const wrapped = modulehot? : app
Start development server:
npm start
Go to http://localhost:8080 and start hacking!
Infrastructure
startApp(app, options)
Render the app into DOM, and wire up HMR in development environment.
app
is the top level component of the app.
Options
container
: DOM element (or CSS selector) to be mounted, default is#root
.env
: Target window to render, default iswindow
. Inject DOM mocks when using server-side rendering.
enableHMR(init)
Wrap a component, re-render with edited component when the module is built.
init(replaceApp)
: Callback function to setup HMR.
webpackConfig(options)
Return a Webpack config object.
Things removed:
case-sensitive-paths-webpack-plugin
postcss-safe-parser
react-dev-utils
InlineChunkHtmlPlugin
InterpolateHtmlPlugin
WatchMissingNodeModulesPlugin
ModuleScopePlugin
getCSSModuleLocalIdent
Things added:
- PnP
pwa-utils
formanifest.json
andindex.html
Options
name
: Name of the application, will appear as tab title and home screen app name.
Optional Options
outputPath
:output.path
of Webpack, default towww
for Cordova.webApp
: Options for generatingmanifest.json
workbox
: Options forGenerateSW
plugin fromworkbox-webpack-plugin
env
: Names of used environment variables, will be passed to EnvironmentPlugin
Babel Options
Option of JSX is {pragma: 'h', pragmaFrag: 'fragment'}
, import h
and fragment
to use JSX.
</>
Redux Bindings
Provider to use Redux with Hooks.
<StoreProvider reducer={{}} initialState={{}} actions={[]}>
Create a Redux store and make it available to nested components, and wire up HMR in development environment.
Props
reducer
(function): The reducer function.reducer
(object): SeecombineReducers
.initialState
actions
: Additional actions to populate initial state.init(replaceReducer)
: Setup HMR,
Example
const init = modulehotaccept'./reducers' replaceReducer const app = <StoreProvider reducers=reducers init=init> <App /> </StoreProvider>
useStore()
Get the store
object.
useStoreState(selector, props)
Subscribe to store updates. returns the object selector
returned.
selector
is the function to derive data from state.
Every time the store is updated, selector
is called to get derived data,
and initiate update of the component if derived data is changed(shallow equality).
If selector
returned with new objects, then the component will update on every store update. So selector
should compose result with objects from state, or use reselect
.
The Stack
Redux binding, routing, shared state and some essential features, all integrated into one provider, and completely opted-in.
<StackProvider reducer={{}} initialState={{}} actions={[]}>
Replace <StoreProvider>
with this to enable following features.
Routing
Declarative routing like React Router, provides minimal features and costs minimal.
<Route path="/" exact render={}>
<NavLink to="/" type="a" others={}>
navigate(to)
An action creator for history.push()
.
to
is same as to
of <NavLink>
.
Shared State
Shared state with minimal code, managed by Redux.
Data is modeled as collections of documents, you can store data to state and retrieve it.
A collection is an ordered set of documents of the same type, a document can be referenced by many collections.
A document is an serializable JavaScript object that have unique id
(among objects of the same type).
Avoid relying on state shape, it might change, always retrieve collections from the state with selectors.
[state, setState] = useSharedState(id, initialValue)
Global version of useState
, state
value is shared across components.
On first render, state will be set to initialValue
if the value in the state is undefined
.
Example
const PriceInput = const price setPrice = return <input ="number" = = /> const AmountInput = const amount setAmount = return <input ="number" = = /> const SubTotal = const price = const amount = return price * amount const Order = <div> <label> Price: <PriceInput /> </label> <label> Amount: <AmountInput /> </label> <div>Sub total: <SubTotal /> </div></div>
getDocument({type='app', id='shared'})
Get document values of specified type
and id
.
getCollection(state, {type, name='default'})
Get collection of specified name, returns a list of id
and an object contains values of each id
Example
updateDocument({type='app', id='shared', values})
Update specified document in state, will merge and overwrite existing values.
replaceCollection({name='default', type='app', documents})
type
can be omitted when replacing existing collections.
const dispatch = // Update posts
Visual Effects
<CountTo value="0" step="1">
Renders a numbers that counts up from 0.
<ActiveAbove>
Wrap nested elements with <div>
, add active
class only when the element is scrolled above of fold.