@rawewhat/stora
v0.2.4
Published
StoRa is a redux replacement based on react hooks
Downloads
2
Maintainers
Readme
StoRa
is a global state management library with no dependency and written purely on React hooks.
How it works?
- in every component that call useStora() hook will subscribe to the global store on component mounted.
- anytime you call set() to change state, all components that subscribed to global store will re-render.
- when subscribed component unmounted, global store will un-subscribe it to prevent re-render.
- with proper usage of useMemo() and useCallback() hook, we can control re-render to quantum level.
so basically, only mounted component that call useStora() hook will re-render when state change
What's next?
- implement automatically memoization with useMemo and useCallback.
- state specific component re-render, currently re-render all subscribed component.
Changelog
Content
- Install
- Setup
- Usage
- API
- Demo
- Example
- Acknowledgment
- License
Install
using npmnpm i @rawewhat/stora
using yarnyarn add @rawewhat/stora
Setup
1. Initialize states and actions in stora.config.js
create a stora config file in either root or src directory of your project.
- [project_root]/stora.config.js
or - [project_root]/src/stora.config.js
export default {
// This will be where you initialize your states
states: {
testComponent: {
testState: 'testState',
},
demoComponent: {
demoState: 'demoState',
},
},
// This will be where you initialize your actions
actions: {
testComponent: {
testAction: (stora) => {
console.log('stora', stora)
},
},
demoComponent: {
demoAction: ({ states, actions }) => {
console.log('states', states, 'actions', actions)
},
},
},
// If you need to do something before global state initialization
init: (stora) => {
console.log('stora', stora)
},
}
Note: states and actions object use component based naming convention
2. Initialize states and actions in top-level component
if you don't want to use stora.config.js to initialize your states and actions, use below syntax instead
- change your App.js or any top-level component
import React from 'react'
import useStora from '@rawewhat/stora'
// you can also import it from another place such as your config directory
import { initialStates, initialActions, initializer } from '../config'
const initialStates = {
testComponent: {
testState: 'testState',
},
demoComponent: {
demoState: 'demoState',
},
}
const initialActions = {
testComponent: {
testAction: (stora) => {
console.log('stora', stora)
},
},
demoComponent: {
demoAction: ({ states, actions }) => {
console.log('states', states, 'actions', actions)
},
},
}
const initializer = (stora) => {
console.log('stora', stora)
}
const App = (props) => {
const [states, actions] = useStora({
states: initialStates,
actions: initialActions,
init: initializer,
})
console.log('states', states, 'actions', actions)
return <span>Stora is awesome!</span>
}
export default App
Note: states and actions object use component based naming convention
3. Next.js server-side rendering
to get it working with server-side rendering, we need to make a custom HOC to wrap our _app.js.
if you don't use _app.js, you need to wrap the HOC in every pages.
- create a HOC in anywhere you want, for example src/service/hoc.js
import React from 'react'
import useStora from '@rawewhat/stora'
// Import our stora config here
import config from '../stora.config'
export const withStora = (PageComponent, { ssr = true } = {}) => {
const WithStora = (props) => {
const { states, actions, init } = config
useStora({ states, actions, init })
return <PageComponent {...props} />
}
if (process.env.NODE_ENV !== 'production') {
const displayName =
PageComponent.displayName || PageComponent.name || 'Component'
WithStora.displayName = `withStora(${displayName})`
}
if (ssr || PageComponent.getInitialProps) {
WithStora.getInitialProps = async (context) => {
const pageProps =
typeof PageComponent.getInitialProps === 'function'
? await PageComponent.getInitialProps(context)
: {}
return {
...pageProps,
}
}
}
return WithStora
}
- after that wrap your _app.js or page component with withStora HOC
in _app.js
import React from 'react'
import NextApp from 'next/app'
// import our withStora hoc from service/hoc.js
import { withStora } from '../service/hoc'
class App extends NextApp {
static async getInitialProps(appContext) {
const appProps = await NextApp.getInitialProps(appContext)
return { ...appProps }
}
render() {
const { Component, pageProps } = this.props
return <Component {...pageProps} />
}
}
// wrap our app with withStora HOC here
export default withStora(App)
in each page component
import { withStora } from '../service/hoc'
import HomeScreen from './screens/Home'
export default withStora(HomeScreen)
Usage
after initialized states and actions in stora.config.
- just import useStora in any component you want to use it
import useStora from '@rawewhat/stora'
- then run it in your function component
const [states, actions] = useStora()
API
1. In Actions Config
1.1 set(string, obj)
change only one component states using key and value arguments
const EXAMPLE_ACTIONS = {
exampleComponent: {
exampleAction: ({ set }) => {
set('testComponent', {
testState: 'changed',
})
},
},
}
any previous states in testComponent will be added automatically
1.2 set(obj)
change multiple component states with new states object
const EXAMPLE_ACTIONS = {
exampleComponent: {
exampleAction: ({ set }) => {
set({
testComponent: {
testState: 'changed',
},
demoComponent: {
demoState: 'changed',
},
})
},
},
}
any previous component states will be added automatically
1.3 get(string)
get only one component states using string name
const EXAMPLE_ACTIONS = {
exampleComponent: {
exampleAction: ({ get }) => {
const testComponent = get('testComponent')
},
},
}
1.4 get([string, string])
get multiple component states using arrays of string name
const EXAMPLE_ACTIONS = {
exampleComponent: {
exampleAction: ({ get }) => {
const {
testComponent: { testState },
demoComponent: { demoState },
} = get(['testComponent', 'demoComponent'])
},
},
}
2. In React Component
2.1 [{ component: { state }}]
access states of specific component or even multiple component states using destructuring syntax.
const [
{
testComponent: { testState },
demoComponent: { demoState },
},
actions,
] = useStora()
2.2 [{ component: { action }}]
access actions of specific component or even multiple component actions using destructuring syntax.
const [
states,
{
testComponent: { testAction },
demoComponent: { demoAction },
},
] = useStora()
2.3 [, actions]
skip states object and directly access actions.
const [, actions] = useStora()
2.4 actions.component.action()
use dot to access states and actions
const [states, actions] = useStora()
console.log('testState:', states.testComponent.testState)
actions.demoComponent.demoAction()
3. useStora({ mutate: { obj, func }})
sometimes you want to add more states, actions or both when using useStora hook on a component
- mutate for adding additional states and actions.
Example: adding another component states
const [
{
newComponent: { newState }, // here you have access to the new added state
testComponent: { testState },
demoComponent: { demoState },
},
actions,
] = useStora({
mutate: {
newComponent: {
newState: 'newState',
},
},
})
Example: adding another component actions
const [
,
{
newComponent: { newActions }, // here you have access to the new added action
testComponent: { testAction },
demoComponent: { demoAction },
},
] = useStora({
mutate: {
newComponent: {
newAction: (stora) => {
console.log('stora', stora)
},
},
},
})
Example: you can even mix states and actions inside mutate object
const [
{
newComponent: { newState }, // here you have access to the new added state
testComponent: { testState },
demoComponent: { demoState },
},
{
newComponent: { newActions }, // here you have access to the new added action
testComponent: { testAction },
demoComponent: { demoAction },
},
] = useStora({
mutate: {
newComponent: {
newState: 'newState',
newAction: (stora) => {
console.log('stora', stora)
},
},
},
})
by checking mutation object type, useStora is smart enough to know whether you want to add state or action
4. useStora({ query: { string | [string, string] }})
if you want to return only some component states from useStora hook
- query for selecting specific component states
Example: select only one component states
const [states] = useStora({
query: 'testComponent',
})
// states for this component contains only testComponent states
const {
testComponent: { testState },
} = states
Example: select multiple component states
const [states] = useStora({
query: ['testComponent', 'demoComponent'],
})
// states for this component contains only testComponent and demoComponent states
const {
testComponent: { testState },
demoComponent: { demoState },
} = states
5. useStora({ store: true })
if you want to get stora instance in a non-component javascript file
- store for getting an instance of stora
Example: get access to stora from anywhere.
const stora = useStora({ store: true })
const { actions, states } = stora
Demo
I wrote a simple router using StoRa + RoutRa in this demo project.
git clone [email protected]:rawewhat/routra.git
to clone the projectcd demo
change to demo directoryyarn install
to install dependenciesyarn dev
it will be ready on http://localhost:1234
Example
import React, { useEffect } from 'react'
import useStora from '@rawewhat/stora'
const App = () => {
const [, actions] = useStora()
useEffect(() => {
actions.testing()
}, [])
return (
<div>
StoRa Demo
<Test />
</div>
)
}
const Test = () => {
const [states, actions] = useStora({
mutate: {
love: () => {},
},
})
console.log('states', states, 'actions', actions)
return (
<div>
Test
<Test2 />
</div>
)
}
const Test2 = () => {
const [states, actions] = useStora({
mutate: {
test100: {
test111: 'test111',
},
},
query: ['test100'],
})
console.log('states', states, 'actions', actions)
return <div>Test2</div>
}
export default App
Acknowledgement
This library is written and improved based on an article on medium by André Gardi
License
MIT License
-----------
Copyright (c) 2019 Cheng Sokdara (https://rawewhat-team.com)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.