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