mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Example: Form handler with Redux and React-bootstrap (#2949)
* Example: Form handler with Redux and React-bootstrap * inputChange refactoring * Destructuring inputChange
This commit is contained in:
parent
d03ce5386d
commit
4bd30c8713
30
examples/form-handler/README.md
Normal file
30
examples/form-handler/README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/form-handler)
|
||||||
|
|
||||||
|
# Form Handler
|
||||||
|
|
||||||
|
## 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/form-handler
|
||||||
|
cd form-handler
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Sometimes handle multiple forms can be tricky, the idea is to have a global reducer
|
||||||
|
with the name of each form and the inputs of it; making accessible everywhere.
|
5
examples/form-handler/actions/formActionsCreators.js
Normal file
5
examples/form-handler/actions/formActionsCreators.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { INPUT_VALUE } from '../constants'
|
||||||
|
|
||||||
|
export const inputChange = (title, name, val) => dispatch => {
|
||||||
|
return dispatch({type: INPUT_VALUE, title, name, val})
|
||||||
|
}
|
1
examples/form-handler/actions/index.js
Normal file
1
examples/form-handler/actions/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './formActionsCreators'
|
26
examples/form-handler/components/DisplayForm.js
Normal file
26
examples/form-handler/components/DisplayForm.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import React, {Component} from 'react'
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
class DisplayForm extends Component {
|
||||||
|
render () {
|
||||||
|
const { state } = this.props
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col lg={8} lgOffset={2}>
|
||||||
|
<pre>{JSON.stringify(state, null, 2) }</pre>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(DisplayForm)
|
17
examples/form-handler/components/Header.js
Normal file
17
examples/form-handler/components/Header.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Col, Row, Jumbotron } from 'react-bootstrap'
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row style={{ marginTop: '10px' }}>
|
||||||
|
<Col lg={8} lgOffset={2}>
|
||||||
|
<Jumbotron style={{ borderRadius: '15px' }}>
|
||||||
|
<h1 style={{textAlign: 'center'}}>Form Handler</h1>
|
||||||
|
</Jumbotron>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
26
examples/form-handler/components/Social.js
Normal file
26
examples/form-handler/components/Social.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
|
||||||
|
import Input from '../handlers/Input'
|
||||||
|
|
||||||
|
const Social = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col lg={8}>
|
||||||
|
<Input controlLabel='Facebook' title='social' name='facebook' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8}>
|
||||||
|
<Input controlLabel='Instagram' title='social' name='instagram' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8}>
|
||||||
|
<Input controlLabel='Twitter' title='social' name='twitter' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8}>
|
||||||
|
<Input controlLabel='Github' title='social' name='github' />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Social
|
26
examples/form-handler/components/UserForm.js
Normal file
26
examples/form-handler/components/UserForm.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
|
||||||
|
import Input from '../handlers/Input'
|
||||||
|
|
||||||
|
const UserForm = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row>
|
||||||
|
<Col lg={8} lgOffset={4}>
|
||||||
|
<Input controlLabel='Name' title='user' name='name' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8} lgOffset={4}>
|
||||||
|
<Input controlLabel='Last name' title='user' name='lastName' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8} lgOffset={4}>
|
||||||
|
<Input controlLabel='Email' type='email' title='user' name='email' />
|
||||||
|
</Col>
|
||||||
|
<Col lg={8} lgOffset={4}>
|
||||||
|
<Input controlLabel='Password' type='password' title='user' name='password' />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserForm
|
34
examples/form-handler/components/index.js
Normal file
34
examples/form-handler/components/index.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import React, {Component} from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
import { Col, Row } from 'react-bootstrap'
|
||||||
|
|
||||||
|
import Header from './Header'
|
||||||
|
import DisplayForm from './DisplayForm'
|
||||||
|
|
||||||
|
import UserForm from './UserForm'
|
||||||
|
import Social from './Social'
|
||||||
|
|
||||||
|
class Main extends Component {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>Form Handler</title>
|
||||||
|
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css' />
|
||||||
|
</Head>
|
||||||
|
<Header />
|
||||||
|
<DisplayForm />
|
||||||
|
<Row>
|
||||||
|
<Col lg={6}>
|
||||||
|
<UserForm />
|
||||||
|
</Col>
|
||||||
|
<Col lg={6}>
|
||||||
|
<Social />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Main
|
1
examples/form-handler/constants/index.js
Normal file
1
examples/form-handler/constants/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export const INPUT_VALUE = 'INPUT_VALUE'
|
38
examples/form-handler/handlers/Input.js
Normal file
38
examples/form-handler/handlers/Input.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React, {Component} from 'react'
|
||||||
|
import { FormGroup, ControlLabel, FormControl } from 'react-bootstrap'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { bindActionCreators } from 'redux'
|
||||||
|
|
||||||
|
import { inputChange } from '../actions'
|
||||||
|
|
||||||
|
class Input extends Component {
|
||||||
|
inputChange = (e) => {
|
||||||
|
const { inputChange, title, name } = this.props
|
||||||
|
inputChange(title, name, e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormGroup controlId='formBasicText'>
|
||||||
|
<ControlLabel>{this.props.controlLabel}</ControlLabel>
|
||||||
|
<FormControl
|
||||||
|
disabled={this.props.disabled}
|
||||||
|
type={this.props.type || 'Text'}
|
||||||
|
placeholder={this.props.controlLabel}
|
||||||
|
onChange={this.inputChange}
|
||||||
|
value={this.props.val}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
inputChange: bindActionCreators(inputChange, dispatch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(Input)
|
26
examples/form-handler/package.json
Normal file
26
examples/form-handler/package.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "redux-form",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "next start -p 8000",
|
||||||
|
"dev": "node server.js",
|
||||||
|
"build": "next build"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.15.4",
|
||||||
|
"next": "^3.2.2",
|
||||||
|
"next-redux-wrapper": "^1.3.2",
|
||||||
|
"react": "^15.6.1",
|
||||||
|
"react-bootstrap": "^0.31.3",
|
||||||
|
"react-dom": "^15.6.1",
|
||||||
|
"react-redux": "^5.0.6",
|
||||||
|
"redux": "^3.7.2",
|
||||||
|
"redux-thunk": "^2.2.0"
|
||||||
|
}
|
||||||
|
}
|
14
examples/form-handler/pages/index.js
Normal file
14
examples/form-handler/pages/index.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import withRedux from 'next-redux-wrapper'
|
||||||
|
|
||||||
|
import Main from '../components'
|
||||||
|
|
||||||
|
import { initStore } from '../store'
|
||||||
|
|
||||||
|
class Index extends Component {
|
||||||
|
render () {
|
||||||
|
return <Main />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRedux(initStore, null)(Index)
|
15
examples/form-handler/reducers/formReducer.js
Normal file
15
examples/form-handler/reducers/formReducer.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { INPUT_VALUE } from '../constants'
|
||||||
|
|
||||||
|
export default (state = {}, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case INPUT_VALUE:
|
||||||
|
return { ...state,
|
||||||
|
[action.title]:
|
||||||
|
{ ...state[action.title],
|
||||||
|
[action.name]: action.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
6
examples/form-handler/reducers/index.js
Normal file
6
examples/form-handler/reducers/index.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { combineReducers } from 'redux'
|
||||||
|
import formReducer from './formReducer'
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
formReducer
|
||||||
|
})
|
28
examples/form-handler/server.js
Normal file
28
examples/form-handler/server.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
const express = require('express')
|
||||||
|
const next = require('next')
|
||||||
|
|
||||||
|
const dev = process.env.NODE_ENV !== 'production'
|
||||||
|
const app = next({ dev })
|
||||||
|
const handle = app.getRequestHandler()
|
||||||
|
|
||||||
|
app.prepare()
|
||||||
|
.then(() => {
|
||||||
|
const server = express()
|
||||||
|
|
||||||
|
server.get('/', (req, res) => {
|
||||||
|
return handle(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.get('*', (req, res) => {
|
||||||
|
return handle(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(3000, (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
console.log('> Ready on http://localhost:3000')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((ex) => {
|
||||||
|
console.error(ex.stack)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
7
examples/form-handler/store.js
Normal file
7
examples/form-handler/store.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { createStore, applyMiddleware } from 'redux'
|
||||||
|
import thunkMiddleware from 'redux-thunk'
|
||||||
|
import reducer from './reducers'
|
||||||
|
|
||||||
|
export const initStore = (initialState = {}) => {
|
||||||
|
return createStore(reducer, initialState, applyMiddleware(thunkMiddleware))
|
||||||
|
}
|
Loading…
Reference in a new issue