1
0
Fork 0
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:
Davide Bertola 2017-02-21 05:13:05 +01:00 committed by Guillermo Rauch
parent 7aa9f5424d
commit d4b018e91f
10 changed files with 223 additions and 0 deletions

View 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.

View 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

View 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

View 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"
}
}

View 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)

View 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)

View file

@ -0,0 +1,6 @@
const counterIncrement = ({counter}, dispatch) => {
return { counter: counter + 1 }
}
export default counterIncrement

View file

@ -0,0 +1,8 @@
const getInitialState = () => {
return {
title: '',
counter: 0
}
}
export default getInitialState

View 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

View file

@ -0,0 +1,6 @@
const setTitle = (newTitle) => ({title}) => {
return { title: newTitle }
}
export default setTitle