I have always been fascinated with one’s ability to create or recreate. Software development for gives me a way to translate my imaginations into useful tangible things. The feeling I get after going through challenges to figure out how to put my ideas together, is incomparable. Full-stack development is the combination of both front-end technologies and back-end technologies to create a fully functional application. In this tutorial, we are going to combine front-end technology called React and Redux with a back-end technology called Rails to create a simple sign-up system. Note: This is an advanced topic, basic to intermediate knowledge in React Redux and Ruby on rails is a prerequisite for this tutorial. Visit this link for React tutorial , for Redux tutorial and rails tutorial . In summary, React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called “components”. Each component may have data specific to it called or data from other components as or it may not have data at all. Redux is a predictable state container for JavaScript apps. On the other hand, Rails is just a back-end technology for persisting data. state props Let us build a simple signup system with the three technologies mentioned above. In this tutorial, we will create a react API that communicates with rails API to perform sign up. Our Sign up UI will be a form with the following fields ( , and ). username email , password, image Steps for setting up Front-end Let’s start by creating a sign-up page. Create action creators and reducers to update the redux state Send user data to the back-end for persistence with Axios. Steps for setting up Back-end Create your rails API Create a DB for API Create a user table and model Migrate table Create a users_controller to handle data persistence Front-end implementation Create react app make sure you have node install and create a react project with the command below npx create-react-app signup This command will generate your basic react-app that you can start building your application on. Create the UI for sign up create a file to house your sign-up component .ie ./src/SignUp.js create a react class component inside the SignUp.js The sign-up component at this moment is going to be a basic react application that renders a form and has event handlers to track the changes in the state of each input field. React, { Component } ; { (props) { (props); .state = { : , : , : , : , }; } handleUsernameChange = { .setState({ : e.target.value, }); } handleEmailChange = { .setState({ : e.target.value, }); } handlePasswordChange = { .setState({ : e.target.value, }); } handleAvatarChange = { .setState({ : e.target.files[ ], }); } render() { ( <h1>{message}</h1> <input onChange={this.handleUsernameChange} type="text" name="username" placeholder="Username" required /> <input onChange={this.handleEmailChange} type="email" name="email" placeholder="Email" required /> <input onChange={this.handlePasswordChange} type="password" name="password" placeholder="Password" required /> <input onChange={this.handleAvatarChange} type="file" accept="/images/*" /> <button type="submit"> Create Account </button> </form> ); } } export default Signup; import from 'react' class Signup extends Component constructor super this username '' email '' password '' picture '' ( ) => e this username ( ) => e this email ( ) => e this password ( ) => e this picture 0 return < = > form onSubmit {this.onSubmitHandler} Connect sign-up component to Redux Bind React to Redux by issuing the command below, then import and use the method from library to bind React component(sign-up component) to Redux connect react-redux npm i react-redux import inside the connect SignUp.js. import { connect } from 'react-redux'; with this set up we can now or after your Redux store is set up mapStateToProps mapDispatchToProps, . At this point we have to set up Redux action and Redux reducers to be able to either or In other words, read from the Redux store or send data to the Redux store. Inside the the Redux action we will dispatch a function to our reducer as we also persist data to our back-end Rails Api with . let us install axios for enable us use axios. mapStateToProps mapDispatchToProps. axios npm i axios read more on axios from the axios documentation Let create these files inside now we implement the Redux actions as ./src/redux/action.js, ./src/redux/reducer.js, axios ; CREATE_USER = ; CREATE_USER_ERROR = ; createUser = (dispatch) => { { dispatch({ : CREATE_USER, ...newUser }); response = axios({ : , : , : { : newUser }, : , }); { token } = response.data; localStorage.setItem( , token); } { dispatch({ : CREATE_USER_ERROR }); } }; { createUser } import from 'axios' const 'CREATE_USER' const 'CREATE_USER_ERROR' const => newUser async try type const await method 'POST' url 'Api route for posting data to database' data user crossdomain true const 'jwt' catch type export The createUser method is going to send data to our rails API and wait for a response. I also sends data to update the Redux store. We are going to configure our API to return some data that would contain information about whether our request from the front end to sign-up a user was successful or not. Json If successful, we will login the user right after successful signup and maintain a session for the user. To maintain a session for user, we store the token generated from the back-end response(returned as part of our positive response) inside . This is handled in these lines localStorage const { token } = response.data; localStorage.setItem('jwt', token); we are storing tho token in the localstorage hash on the key 'jwt' Now we have to create our reducer for the CREATE_USER and CREATE_USER_ERROR initialState = { : , : { : , : , : , : , } }; authReducer = { (action.type) { : { ...state.user, : , : { : action.username, : action.email, : action.password, : action.picture, }, }; : { : , }; : state; } }; authReducer; const isLogin false user username '' email '' password '' picture '' const ( ) => state = initialState, action switch case 'CREATE_USER' return isLogin true user username email password picture case 'CREATE_USER_ERROR' return isLogin false default return export default CREATE_USER_ERROR is dispatched when a negative response is returned from our server. With this in place we almost good to go. But there would be two problems if we try to signup a user. we need to properly handle the picture attribute and set up redux-thunk middleware since we are dispatching a function in our createUser action creator. To handle our image upload, we will store our image to cloudinary and copy the path of the image in cloudinary to our database. storing images directly into the server is not recommended. you it is good to use third parties such amazon s3, cloudinary, etc... for this we will update our S to include ignUp.js handleImageUpload() and onSubmitHandler(). will be responsible for storing the image file selected to cloudinary and return the URL reference of that image. This URL reference is then sent as part of data dispatch to redux and the rails back-end. handleImageUpload() this method on the other hand will be responsible for sending data to our action creator. We shall import the action creator method, and passed data to from the form to to be update the redux store and send data to the rails API. In this app will mapped to , hence we will reference create user as . onSubmitHandler(), createUser createUser({sign up state data}) createUser newUser NewUser mapDispatchToProps= ({ : newUser }) => dispatch createUser we destructure state data and get the URL reference, pass it as an argument to newUser, for dispatch. {username, email, password,picture}= .state newUser({username,email,password,picture}) const this //get image path, from handleImageUpload Check below for full implementation. handleImageUpload = (imageFile) => { formData = FormData(); formData.append( , imageFile); formData.append( , ); response = axios({ : , : , : formData, }); response.data.secure_url; } onSubmitHandler = (e) => { e.preventDefault(); {username, email, password } = .state; { picture } = .state; { newUser, user } = .props; imgPath = .handleImageUpload(picture); picture = imgPath; newUser({username, email, password, picture}); (user.isLogin === ) { { history } = .props; history.push( ); } { .setState( { : , }, ); } } async const new 'file' 'upload_preset' 'avatar' const await url 'your cloudinary api url' method 'POST' data return async const this let this const this const await this await if true const this '/' else this message 'welcome' with this in place we can and to dispatch our data to redux, and read data from the store. Check below. mapDispatchToProps() mapStateToProps() mapStateToProps = ({ : state.user, }); mapDispatchToProps = ({ : dispatch(createUser(estate)), }); Signup.propTypes = { : PropTypes.func.isRequired, : PropTypes.object.isRequired, : PropTypes.func.isRequired }; connect(mapStateToProps, mapDispatchToProps)(Signup); const => state user const => dispatch newUser => estate newUser user history export default our resultant code will become like this SignUp.js React, { Component } ; { connect } ; { Link } ; PropTypes ; axios ; { createUser } ; { (props) { (props); .state = { : , : , : , : , : , }; } handleUsernameChange = { .setState({ : e.target.value, }); } handleEmailChange = { .setState({ : e.target.value, }); } handlePasswordChange = { .setState({ : e.target.value, }); } handleAvatarChange = (e) => { .setState({ : e.target.files[ ], }); } handleImageUpload = (imageFile) => { formData = FormData(); formData.append( , imageFile); formData.append( , ); axios.defaults.headers.common.Authorization; response = axios({ : , : , : formData, }); response.data.secure_url; } onSubmitHandler = (e) => { e.preventDefault(); { username, email, password, } = .state; { picture } = .state; { newUser, user } = .props; imgPath = .handleImageUpload(picture); picture = imgPath; newUser({ username, email, password, picture, }); (user.isLogin === ) { { history } = .props; history.push( ); } { .setState( { : , }, ); } } render() { { message } = .state; ( <h1>{message}</h1> <input onChange={this.handleUsernameChange} type="text" required /> <input onChange={this.handleEmailChange} type="email" required /> <input onChange={this.handlePasswordChange} type="password" required /> <input onChange={this.handleAvatarChange} type="file" accept="/images/*" /> <Link to="/login">login</Link> <button type="submit">Create Account</button> </form> ); } } const mapStateToProps = state => ({ user: state.user, }); const mapDispatchToProps = dispatch => ({ newUser: estate => dispatch(createUser(estate)), }); Signup.propTypes = { newUser: PropTypes.func.isRequired, user: PropTypes.object.isRequired, history: PropTypes.object.isRequired, }; export default connect(mapStateToProps, mapDispatchToProps)(Signup); import from 'react' import from 'react-redux' import from 'react-router-dom' import from 'prop-types' import from 'axios' import from '../../redux/actions/authAction' class Signup extends Component constructor super this username '' email '' password '' picture '' message '' ( ) => e this username ( ) => e this email ( ) => e this password async this picture 0 async const new 'file' 'upload_preset' 'avatar' delete const await url 'your cloudinary api url' method 'POST' data return async const this let this const this const await this await if true const this '/' else this message 'welcome' const this return < = > form onSubmit {this.onSubmitHandler} let us create a store and add to allows us to write action creators that return a function instead of an action. move to index.js and set it up like this. redux-thunk middleware React ; ReactDOM ; { createStore, applyMiddleware } ; { Provider } ; thunk ; rootReducer ; App ; initialState = { : { : , : { : , : , : , : , }, }, }; store = createStore(rootReducer, initialState, applyMiddleware(thunk)); ReactDOM.render( <App /> , .getElementById( ), ); import from 'react' import from 'react-dom' import from 'redux' import from 'react-redux' import from 'redux-thunk' import from './redux/reducers/index' import from './component/App' const user isLogin false newuser username '' email '' password '' picture '' const < = > Provider store {store} </ > Provider document 'root' now our front-end is ready, we can now focus on the back-end Create rails Api rails new backend --api --database=postgresql -T rails g model User username:string email:string password:string picture:string rails db:migrate Now we include and in gemfile. jwt gem rack-cors gem then we run in console. bundle install configure cors to accept request from a source of your choice. Cors setting determines which domain should your api grant access to. read more on we will configure our cors to accept request from all domain source by setting rack-cor gem origin '*' in config/application.rb Rails.application.config.middleware.insert_before , Rack::Cors allow origins resource , , %i[get post put patch delete options head] 0 do do '*' '*' headers: :any methods: end end we are going to skip testing and also ignore validations. We are going to focus on the controller and its actions. Let create a controller for our user model rails g controller users we will new a create action to create a user, inside our users_controller.rb we create a create method with this code. = User.new(user_params) new_user.save token = JsonWebToken.encode(user_id: new_user.id) time = Time.now + hours.to_i render json: { : token, : time }, : :ok head(:unprocessable_entity) end end private def user_params params.require(:user).permit( :username, :email, :country, :password, :picture ) end end < class UsersController ApplicationController def create new_user if 24. token time status else create user method accept parameters and save it to database. Then get the user's id, encrypt the id with and add a time stamp to the encrypted user_id. The time stamp determines the time of expiry of the token, in this case, it expires after 24hrs. This means a user's session will be destroyed after 24hrs from sign up. jwt gem we can now set up our route inside config/route.rb resources :users, only: %i[create] Get front end to communicate with back-end run rails routes in console to view the url for your api. In this case our requests is going to be made on post backend_domain_name/users Conclusion Now we will be able to save a users information inside our rails back-end with these steps and returned a token to maintain a session. A similar result can be used for sign in just that for signing in the instead of saving we compare email and password to check if they exist then we create a token that would be used by user maintain a session. Note this token are sent as part of the response to our the front end. The session handling is then than in the front end.