mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
add ‘with-refnux’ example (#1215)
This commit is contained in:
parent
7aa9f5424d
commit
d4b018e91f
54
examples/with-refnux/README.md
Normal file
54
examples/with-refnux/README.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
|
||||
# Refnux example
|
||||
|
||||
## How to use
|
||||
|
||||
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||
|
||||
```bash
|
||||
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-refnux
|
||||
cd with-refnux
|
||||
```
|
||||
|
||||
Install it and run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
|
||||
|
||||
```bash
|
||||
now
|
||||
```
|
||||
|
||||
|
||||
## The idea behind the example
|
||||
|
||||
This example, just like `with-react` and `with-mobx` examples, shows how to manage a global state in your web-application.
|
||||
In this case we are using refnux which is an alternative, simpler, purely functional store state manager.
|
||||
|
||||
We have two very similar pages (page1.js, page2.js). They both
|
||||
|
||||
- show the current application state, including a simple counter value
|
||||
- have a link to jump from one page to the other
|
||||
- have an 'increment' button to increment the state of the counter
|
||||
(it triggers the `counterIncrement` action)
|
||||
|
||||
When running the example, please, increment the counter and note how moving from page 1 to page 2 and back the state is persisted.
|
||||
Reloading any of the pages will restore the initial state coming from the server.
|
||||
|
||||
|
||||
### Implementation details
|
||||
|
||||
Each page uses `withRefnux` helper which wraps the page in a Provider component.
|
||||
Page components are connected to the state using refnux `connect` function.
|
||||
|
||||
In the `store/` directory you can see a simple implmentation of
|
||||
|
||||
- a `getInitialstate` function that returns the initial state of the store. It's used only on SSR
|
||||
- a `counterIncrement` action that increments the counter state whe user pushes the corresponding button
|
||||
- a `setTitle` action that's used to set page title during pages `getInitialProps`
|
||||
|
||||
If you have any comment / question / pull requests please refer to the [original repository](https://github.com/davibe/next.js-example-with-refnux) where this example is developed and maintained.
|
18
examples/with-refnux/helpers/getStore.js
Normal file
18
examples/with-refnux/helpers/getStore.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { createStore } from 'refnux'
|
||||
|
||||
let storeMemoized = null
|
||||
|
||||
const getStore = (initialState) => {
|
||||
let store = null
|
||||
if (typeof window == 'undefined') {
|
||||
store = createStore(initialState)
|
||||
} else {
|
||||
if (!storeMemoized) {
|
||||
storeMemoized = createStore(initialState)
|
||||
}
|
||||
store = storeMemoized
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
export default getStore
|
42
examples/with-refnux/helpers/withRefnux.js
Normal file
42
examples/with-refnux/helpers/withRefnux.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { Provider} from 'refnux'
|
||||
|
||||
import getStore from './getStore'
|
||||
|
||||
// The `withRefnux` "decorator"
|
||||
// - wraps the given Component in a refnux Provider component
|
||||
// - creates a `store` for the Provider handling different server side / client side cases
|
||||
// - runs wrapped component `getInitialProps` as expected
|
||||
// - passes `store` to Component's `getInitialProps` so that it can dispatch actions
|
||||
|
||||
const withRefnux = (getInitialState, Component) => {
|
||||
|
||||
const Wrapper = (props) => {
|
||||
var store = props.store
|
||||
// if getInitialProps was executed on the server we get a store
|
||||
// that's missing non-serializable functions.
|
||||
// Because of this we need to recreate the store based on the
|
||||
// state coming from the server.
|
||||
if (!store.dispatch) {
|
||||
store = getStore(props.store.state)
|
||||
}
|
||||
return <Provider
|
||||
store={store}
|
||||
app={() => <Component {...props.componentProps} />}
|
||||
/>
|
||||
}
|
||||
|
||||
Wrapper.getInitialProps = async function (context) {
|
||||
const store = getStore(getInitialState())
|
||||
var componentProps = {}
|
||||
// honor wrapped component getInitialProps
|
||||
if (Component.getInitialProps) {
|
||||
componentProps = await Component.getInitialProps({ ...context, store })
|
||||
}
|
||||
return { store, componentProps }
|
||||
}
|
||||
|
||||
return Wrapper
|
||||
}
|
||||
|
||||
|
||||
export default withRefnux
|
17
examples/with-refnux/package.json
Normal file
17
examples/with-refnux/package.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "next.js-example-with-refnux",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"next": "next"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"next": "^2.0.0-beta.31",
|
||||
"react": "^15.4.2",
|
||||
"react-dom": "^15.4.2",
|
||||
"refnux": "^1.3.0"
|
||||
}
|
||||
}
|
28
examples/with-refnux/pages/page1.js
Normal file
28
examples/with-refnux/pages/page1.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { connect } from 'refnux'
|
||||
import Link from 'next/link'
|
||||
|
||||
import withRefnux from '../helpers/withRefnux'
|
||||
import getInitialState from '../store/getInitialState'
|
||||
|
||||
// actions
|
||||
import counterIncrement from '../store/counterIncrement'
|
||||
import setTitle from '../store/setTitle'
|
||||
|
||||
const Page1 = connect(
|
||||
(state, dispatch) =>
|
||||
<div>
|
||||
<h3>{state.title}</h3>
|
||||
<p>Current state: {JSON.stringify(state, null, 2)}</p>
|
||||
<button onClick={() => dispatch(counterIncrement)} >Increment</button>
|
||||
<Link href="/page2"><button>go to page 2</button></Link>
|
||||
</div>
|
||||
)
|
||||
|
||||
Page1.getInitialProps = async function (context) {
|
||||
const {store} = context
|
||||
// dispatch actions to store to set it up for this page / route
|
||||
store.dispatch(setTitle('Page 1'))
|
||||
return {} // we have a store, we don't need props!
|
||||
}
|
||||
|
||||
export default withRefnux(getInitialState, Page1)
|
28
examples/with-refnux/pages/page2.js
Normal file
28
examples/with-refnux/pages/page2.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { connect } from 'refnux'
|
||||
import Link from 'next/link'
|
||||
|
||||
import withRefnux from '../helpers/withRefnux'
|
||||
import getInitialState from '../store/getInitialState'
|
||||
|
||||
// actions
|
||||
import counterIncrement from '../store/counterIncrement'
|
||||
import setTitle from '../store/setTitle'
|
||||
|
||||
const Page2 = connect(
|
||||
(state, dispatch) =>
|
||||
<div>
|
||||
<h3>{state.title}</h3>
|
||||
<p>Current state: {JSON.stringify(state, null, 2)}</p>
|
||||
<button onClick={() => dispatch(counterIncrement)} >Increment</button>
|
||||
<Link href="/page1"><button>go to page 2</button></Link>
|
||||
</div>
|
||||
)
|
||||
|
||||
Page2.getInitialProps = async function (context) {
|
||||
const {store} = context
|
||||
store.dispatch(setTitle('Page 2'))
|
||||
return {}
|
||||
}
|
||||
|
||||
|
||||
export default withRefnux(getInitialState, Page2)
|
6
examples/with-refnux/store/counterIncrement.js
Normal file
6
examples/with-refnux/store/counterIncrement.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
const counterIncrement = ({counter}, dispatch) => {
|
||||
return { counter: counter + 1 }
|
||||
}
|
||||
|
||||
export default counterIncrement
|
8
examples/with-refnux/store/getInitialState.js
Normal file
8
examples/with-refnux/store/getInitialState.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
const getInitialState = () => {
|
||||
return {
|
||||
title: '',
|
||||
counter: 0
|
||||
}
|
||||
}
|
||||
|
||||
export default getInitialState
|
16
examples/with-refnux/store/getStore.js
Normal file
16
examples/with-refnux/store/getStore.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { createStore } from 'refnux'
|
||||
|
||||
const storeInitialState = { counter: 0, key: 'value' }
|
||||
|
||||
const getStore = () => {
|
||||
let store = null
|
||||
if (typeof window == 'undefined') {
|
||||
store = createStore(storeInitialState)
|
||||
} else {
|
||||
store = window.store || createStore(storeInitialState)
|
||||
window.store = store
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
export default getStore
|
6
examples/with-refnux/store/setTitle.js
Normal file
6
examples/with-refnux/store/setTitle.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
const setTitle = (newTitle) => ({title}) => {
|
||||
return { title: newTitle }
|
||||
}
|
||||
|
||||
export default setTitle
|
Loading…
Reference in a new issue