Full-stack universal React, Redux, Firebase boilerplate 🔥 Built on top of the awesome reactGo framework
🚧 still in construction
- ReactJS
- Redux
- Firebase
- Universal rendering 🌏
- React Router
- React Router Redux
- react-transform-hmr hot reloading
- Redux-Devtools Chrome Extension
- Webpack
- Express 4.x server
Firebase configuration file exist within app/utils/firebase/config.js
, replace these with your own Firebase credentials.
You can edit the below services inside app/services/firebaseService.js
getFirebaseObject('example-firebase-ref')
.then(exampleObj => ({exampleObj}))
.catch(error => console.error(error))
getFirebaseArray('example-firebase-ref')
.then(arrayofItems => ({ arrayofItems }))
.catch(error => console.log('error', error));
Super basic overview, you can find out more here
app/routes.jsx
<Route path="posts/:id" component={Post} name="Post" fetchData={fetchData} />
app/fetch-data/fetchData.js
// Post container data
case 'Post': {
return getFirebaseObject(`posts/${params.id}`)
.then(post => ({ post }))
.catch(error => console.log('error', error));
}
- Matches component to path
- React Router's onUpdate function (
app/client.jsx
) handles the route change, dispatches initial Redux action creator and runs fetchData - fetchData runs a switch statement on the name prop to request the async data and dispatches results to the Redux store making the data available within the matched container
- Server returns html file to the browser with initial state
A quick tutorial to using Redux forms! ⚡
import React from 'react';
import { reduxForm } from 'redux-form';
class ContactForm extends React.Component {
render() {
const { fields: {name, address, phone}, handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<label>Name</label>
<input type="text" {...name}/>
{name.error && name.touched && <div>{name.error}</div>}
<label>Address</label>
<input type="text" {...address} />
{address.error && address.touched && <div>{address.error}</div>}
<label>Phone</label>
<input type="text" {...phone}/>
{phone.error && phone.touched && <div>{phone.error}</div>}
<button onClick={handleSubmit}>Submit</button>
</form>
);
}
}
ContactForm = reduxForm({
form: 'contact', // the name of your form and the key to
// where your form's state will be mounted
fields: ['name', 'address', 'phone'], // a list of all your fields in your form
validate: validateContact // a synchronous validation function
})(ContactForm);
export default ContactForm;
import React from 'react';
import { connect } from 'react-redux';
import { initialize } from 'redux-form';
import ContactForm from './ContactForm.react';
class App extends React.Component {
handleSubmit(data) {
console.log('Submission received!', data);
this.props.dispatch(initialize('contact', {})); // clear form
}
render() {
return (
<div id="app">
<h1>App</h1>
<ContactForm onSubmit={this.handleSubmit.bind(this)}/>
</div>
);
}
}
export default connect()(App);
import { combineReducers } from 'redux';
import { appReducer } from './app-reducers';
import { reducer as formReducer } from 'redux-form';
let reducers = combineReducers({
appReducer, form: formReducer // this is the form reducer
});
export default reducers;
And the validator module looks like this:
export default function validateContact(data, props) {
const errors = {};
if(!data.name) {
errors.name = 'Required';
}
if(data.address && data.address.length > 50) {
errors.address = 'Must be fewer than 50 characters';
}
if(!data.phone) {
errors.phone = 'Required';
} else if(!/\d{3}-\d{3}-\d{4}/.test(data.phone)) {
errors.phone = 'Phone must match the form "999-999-9999"'
}
return errors;
}
If you ever find yourself needing to prepopulate fields in your form, say for an editing type functionality, you can use the initialize function:
componentWillMount() {
this.props.dispatch(initialize('contact', {
name: 'test'
}, ['name', 'address', 'phone']));
}
ContactForm = reduxForm({
form: 'contact', // the name of your form and the key to
fields: ['name', 'address', 'phone'], // a list of all your fields in your form
validate: validateContact // a synchronous validation function
}, state => ({
initialValues: {
name: state.user.name,
address: state.user.address,
phone: state.user.phone,
},
}))(ContactForm);