I've created a container called siteEdit.js. It handles creating & editing "sites".
I've setup actionCreators that handles taking the form data and submitting it to the API. This works perfectly.
But when you visit the container using a route that contains an ID it will run an actionCreator that will fetch for the "Site" data based on the id param.
This all works as expected, but since I'm using redux, I'm setting the Input value with the props. for example, this.props.title
I'm trying to stay away from using the redux-form package for now.
Container:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {createSite, getSite} from '../../actions/siteActions';
class SiteEdit extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
url: '',
description: '',
approvedUsers: []
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleInputChange = this.handleInputChange.bind(this)
}
componentWillMount() {
if(this.props.params.id) {
this.props.dispatch(getSite(this.props.params.id))
}
}
handleInputChange(e) {
const target = e.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState({
[name]: value
})
}
handleSubmit(e) {
e.preventDefault()
this.props.dispatch(createSite(this.state))
}
render() {
const {title, url, description, approvedUsers} = this.props
return (
<div className="SiteEdit">
<h1>NEW SITE</h1>
<form onSubmit={this.handleSubmit}>
<div className="block">
<label>Site Name</label>
<input
className="input"
type="text"
value={title ? title : this.state.title}
onChange={this.handleInputChange}
name="title" />
</div>
<div className="block">
<label>Site URL</label>
<input
className="input"
type="text"
value={this.state.url}
onChange={this.handleInputChange}
name="url" />
</div>
<div className="block">
<label>Description</label>
<input
className="textarea"
type="textarea"
value={this.state.description}
onChange={this.handleInputChange}
name="description" />
</div>
<div className="block">
<label>Approved Users</label>
<input
className="input"
type="text"
value={this.state.approvedUsers}
onChange={this.handleInputChange}
name="approvedUsers" />
</div>
<button className="button--action">Create</button>
</form>
</div>
)
}
}
const mapStateToProps = (state) => ({
title: state.sites.showSite.title,
url: state.sites.showSite.url,
description: state.sites.showSite.description,
approvedUsers: state.sites.showSite.approvedUsers
})
SiteEdit = connect(mapStateToProps)(SiteEdit)
export default SiteEdit
ActionCreators:
import config from '../config'
import { push } from 'react-router-redux'
const apiUrl = config.api.url
// List all sites
export const LIST_SITES_START = 'LIST_SITES_START'
export const LIST_SITES_SUCCESS = 'LIST_SITES_SUCCES'
export const LIST_SITES_ERROR = 'LIST_SITES_ERROR'
export function sitesListStart(data) {
return { type: LIST_SITES_START, data }
}
export function sitesListSuccess(data) {
return { type: LIST_SITES_SUCCESS, data }
}
export function sitesListError(data) {
return { type: LIST_SITES_ERROR, data }
}
export function listSites() {
return (dispatch) => {
dispatch(sitesListStart())
fetch(`${apiUrl}/listSites`)
.then(res => res.json())
.then(json => {
dispatch(sitesListSuccess(json))
})
.catch(error => {
dispatch(sitesListError)
})
}
}
// Create & Edit Sites
export const CREATE_SITE_START = 'CREATE_SITE_START'
export const CREATE_SITE_SUCESS = 'CREATE_SITE_SUCCESS'
export const CREATE_SITE_ERROR = 'CREATE_SITE_ERROR'
export function siteCreateStart(data) {
return { type: CREATE_SITE_START, data}
}
export function siteCreateSuccess(data) {
return { type: CREATE_SITE_SUCCESS, data}
}
export function siteCreateError(error) {
return { type: CREATE_SITE_ERROR, error}
}
export function createSite(data) {
return (dispatch) => {
dispatch(siteCreateStart())
fetch(`${apiUrl}/createSite`, {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
dispatch(push('/'))
dispatch(siteCreateSuccess())
})
.catch(error => {
dispatch(siteCreateError())
})
}
}
// Get Single Site
export const GET_SITE_START = 'GET_SITE_START'
export const GET_SITE_SUCCESS = 'GET_SITE_SUCCESS'
export const GET_SITE_ERROR = 'GET_SITE_ERROR'
export function getSiteStart(data) {
return { type: GET_SITE_START, data}
}
export function getSiteSuccess(data) {
return { type: GET_SITE_SUCCESS, data}
}
export function getSiteError(error) {
return { type: GET_SITE_ERROR, error}
}
export function getSite(id) {
return (dispatch) => {
dispatch(getSiteStart())
fetch(`${apiUrl}/getSite/${id}`)
.then(res => res.json())
.then(json => {
dispatch(getSiteSuccess(json))
})
.catch(error => {
dispatch(getSiteError())
})
}
}
Reducers:
import {push} from 'react-router-redux'
import {
LIST_SITES_START,
LIST_SITES_SUCCESS,
LIST_SITES_ERROR,
GET_SITE_START,
GET_SITE_SUCCESS,
GET_SITE_ERROR
} from '../actions/siteActions'
const initialState = {
sitesList: {
sites: [],
error: null,
loading: true
},
showSite: {
title: '',
url: '',
description: '',
approvedUsers: [],
loading: true
}
}
export default function (state = initialState, action) {
switch (action.type) {
// List Sites
case LIST_SITES_START:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
loading: true
})
})
case LIST_SITES_SUCCESS:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
sites: action.data,
loading: false
})
})
case LIST_SITES_ERROR:
return Object.assign({}, state, {
error: action.error,
loading: false
})
case GET_SITE_START:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
loading: true
})
})
case GET_SITE_SUCCESS:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
...action.data,
loading: false
})
})
case GET_SITE_ERROR:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
error: action.error,
loading: false
})
})
default:
return state
}
}
<input className="input" type="text" value={title ? title : this.state.title} onChange={this.handleInputChange} name="title" />the value beingthis.props.titleand not letting me update the state. I think I need to set the state from the props?