Skip to content

Commit 721f71f

Browse files
committed
React Daily UI -- 001
1 parent d1170a5 commit 721f71f

File tree

12 files changed

+557
-2
lines changed

12 files changed

+557
-2
lines changed

‎001-sign-up/.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# See http://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
node_modules
5+
6+
# testing
7+
coverage
8+
9+
# production
10+
build
11+
12+
# misc
13+
.DS_Store
14+
npm-debug.log

‎001-sign-up/README.md

Whitespace-only changes.

‎001-sign-up/daily-ui-sign-up-post.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
---
2+
page_id: react-daily-ui-001
3+
series: react-daily-ui
4+
permalink: 001-sign-up-form
5+
title: React Daily UI - 001 Sign Up Form
6+
description:
7+
published: false
8+
author: sophia
9+
hero_image: /assets/images/articles/react-native-firestack/react-native-firestack-hero.jpg
10+
main_image: /assets/images/articles/react-native-firestack/react-native-firestack-hero.jpg
11+
codeRoot: '__FILE_PATH__'
12+
autotoc: true
13+
fileMetaKeyHeadingsAllowed: true
14+
---
15+
16+
# Fullstack React Daily UI: 001 Sign Up Form
17+
18+
> This post is a part of the [React Daily UI](https://codepen.io/jackoliver/) post series, a joint effort between [Jack Oliver](http://www.jackoliver.info/) and the team at [Fullstack React](https://www.fullstackreact.com/).
19+
> [Follow the whole series here](/articles/react-daily-ui/)
20+
21+
22+
Welcome to React Daily UI, where we go 100 days building 100 beautiful React applications. We're really excited to be partnering with [Jack Oliver](http://www.jackoliver.info/) who is embarking on this ambitious project with us. Jack is designing and writing the code for these applications and we're going to deconstruct each one to highlight the features that are unique to React.
23+
24+
Kicking it off today, we're going to make a sign up form:
25+
26+
[pic]
27+
28+
## Overview
29+
30+
This sign up form has a few different features that are unique to React. The sign up form itself is a modal that uses CSS animations and slides down when the page loads and slides up after the user logs in. We are going to learn about the following features for this sign up form:
31+
32+
* Using `state` and `props`
33+
* CSS animations
34+
* Event handling
35+
* Native browser validation for forms
36+
37+
38+
## Mounting with `state`
39+
40+
React uses the concept of _components_ which, conceptually are containers for our data and UI. Each component has several properties and methods that we will take advantage of in this post and for the rest of the series.
41+
42+
Additionally, a React component can define it's own state using a `state` property for handling stateful components, such as our form element today.
43+
44+
Using the `state` property allows us to manipulate a React component's view and data associated with the view to keep track of the local state of the component.
45+
46+
> ## What is `state`?
47+
>
48+
> When we refer to a component's `state`, we mean a snapshot of the instance of the component on the page.
49+
>
50+
> In regular HTML (without React), when we have a text `<input />` box on a page, the _state_ of the `<input />` element is that the _value_ of the `<input />` component is a blank string (i.e. `""`).
51+
> When our user types into the `<input />` box, the `state` changes for the `<input />` box to set the value to the keystroke the user made.
52+
>
53+
> React's components can define their own `state` which we'll use in today's post, and others in the future. When we use state in a React component the component is said to be _stateful_.
54+
55+
In this particular instance we are going to use the component's `state` to show and hide the form on the page.
56+
57+
Let's create the sign up form by first wrapping it in a parent `App` component. This way we can define some methods in this parent component that we can use to show or hide the sign up modal view.
58+
59+
The basic stateful `App` component looks like this:
60+
61+
{lang=javascript,crop-query=(.App)}
62+
<<[](src/App.js)
63+
64+
React components have a method that is frequently used called `getInitialState`.
65+
66+
React expects us to return a JavaScript object from this method that stores any sort of data we want to manipulate or display in the component.
67+
68+
Let's tell React that the `App` component keeps a single item in it's local state, a boolean we will call `mounted`.
69+
70+
{lang=javascript,crop-query=(.App .getInitialState)}
71+
<<[](src/App.js)
72+
73+
React components also have 'lifecycle hooks' where we can define custom functionality during the different phases of the component. These methods are executed at specific points in a component's lifecycle [1](#references).
74+
75+
One of these hooks is the `componentDidMount` method is executed just after the component has been rendered to the page. In order to define functionality during the lifecycle, we need to define the method in the component:
76+
77+
{lang=javascript,crop-query=(.App .componentDidMount)}
78+
<<[](src/App.js)
79+
80+
This method is where we set our `mounted` state to `true` as now our form is inserted into the DOM and our component is prepared to show it. We change the state of our data using a component method `setState`
81+
82+
{lang=javascript,crop-query=window(.componentDidMount,1,1)}
83+
<<[](src/App.js)
84+
85+
Although a component's state is available via `this.state`, we should treat `this.state `as a readonly object and only ever change the state using the `setState` method available on a React component.
86+
87+
The `setState` method sends the state object into a queue to be batched for DOM updates, so modifying or changing any portion of a component's state should only happen via `setState`.
88+
89+
We only want to show the modal if it is mounted, so we can include a conditional rendering statement in our component that renders the form if and only if the component has been mounted.
90+
91+
{lang=javascript,crop-query=window(.App .render,2,6)}
92+
<<[](src/App.js)
93+
94+
This conditional statement creates the child component if the `App` component has been rendered to the DOM. Our child component, `Modal` contains a form inside of it.
95+
96+
## The Form
97+
98+
Our Modal component contains a form that looks familiar if you've built a form with HTML before. However, there are a few differences between this form and a typical HTML form. The form contains a `props` attribute as well as a custom `<Input/>` component that also uses `this.props`.
99+
100+
### `this.props`
101+
102+
In addition to the `state` property, React components have another property called `props`.
103+
104+
The difference between `state` and `props` can be a little confusing at first. `props` are used to pass down data and event handlers from parent components to child components. `state` on the other hand is used to manipulate the current state of a component.
105+
106+
> Notice that a child component does _not_ pass any data back up to it's parent component. This means we can only ever pass data _down_ the component tree. This pattern of data passing is called _one-way databinding_.
107+
>
108+
> A child component _can_ inform the parent component about an update it makes, but cannot change the data itself. We'll look at this process next:
109+
110+
Another aspect about the `props` attribute that can be a little confusing is passing down a function as a prop. When our `Modal` component is created, we pass down the `App` component's `handleSubmit` function as a prop:
111+
112+
{lang=javascript,crop-query=window(.App .render,4,4)}
113+
<<[](src/App.js)
114+
115+
Then, in our form itself, the onSubmit event handler is given the submit callback function we passed down as a prop:
116+
117+
{lang=javascript,crop-query=window(.Modal .render,3,5)}
118+
<<[](src/App.js)
119+
120+
The function we define as `onSubmit` can then be called by the child `<Modal />` component when the user hits the submit button in the view. The actual code for the function is found in the parent component:
121+
122+
{lang=javascript,crop-query=(.App .handleSubmit)}
123+
<<[](src/App.js)
124+
125+
When the user clicks the submit button this event handler is called and the `mounted` variable gets set back to `false`.
126+
127+
We also have three `Input` components which we'll define next.
128+
129+
### Building reusable inputs
130+
131+
We can define a reusable component, which can take in `props` that get passed down from the parent `<form />` component. Three `props` we'll want to pass into the `<Input />` component are:
132+
133+
* name
134+
* type
135+
* placeholder
136+
137+
These particular values are JavaScript strings, unlike the `onSubmit` event handler where we passed a function.
138+
139+
These props allow us to create reuseable components since we just have to pass in the type of input (either text, email or password), the name we want to associate with the input element, and the placeholder to a normal `input` element.
140+
141+
{lang=javascript,crop-query=(.Input)}
142+
<<[](src/App.js)
143+
144+
### Native browser validation
145+
146+
Two of the input types we are using with this form are slightly different than the normal `text` type: `password` and `email`. With these input types, the browser will automatically do some validation and render the inputs slightly differently than a normal `text` type. Using the `email` type will make the browser do some simple validation of the text, including checking for the `@` symbol. Using the `password` type automatically masks the input.
147+
148+
## Animating with `ReactCSSTransitionGroup`
149+
150+
Animations in React are not always as simple as just adding a few transitions and translations to our CSS files. Since React's algorithm works differently than other JavaScript libraries such as JQuery, React requires some DOM nodes to be removed/added to the DOM instead of manipulated.
151+
152+
Adding animations via CSS files aren't always straight-forward. To address this problem, React provides an addon library to ease with the difficulties: ReactCSSTransitionGroup.
153+
154+
> The `ReactCSSTransitionGroup` library is not included by default when using the create-react-app tool.
155+
> Although this library is included with the code for this post, to use it in your own projects, we'll need to install it using the `npm` package manager:
156+
>
157+
> ```bash
158+
> npm install react-addons-css-transition-group
159+
> ```
160+
161+
Let's look at how to use ReactCSSTransitionGroup with our form. We'll use a few `props` on the `ReactCSSTransitionGroup` element which define the CSS `transition` name as well as a few `props` to define variables:
162+
163+
{lang=javascript,crop-query=(.App .render)}
164+
<<[](src/App.js)
165+
166+
When the `App` component is mounted, our `child` variable is assigned our Modal component:
167+
168+
{lang=javascript,crop-query=window(.App .render,1,5)}
169+
<<[](src/App.js)
170+
171+
By wrapping the modal in a `ReactCSSTransitionGroup` component, we get access to the animation effect whenever the modal is added and removed from the DOM.
172+
173+
The `ReactCSSTransitionGroup` must be available and mounted in the DOM for the animations to take effect.
174+
175+
If the `ReactCSSTransitionGroup` node is mounted at the same time as it's children, the animations will not work. The `ReactCSSTransitionGroup` has two properties that manage the durations of the animation when the modal enters/leaves the DOM:
176+
177+
{lang=javascript,crop-query=window(.App .render,9,12)}
178+
<<[](src/App.js)
179+
180+
Using ReactCSSTransitionGroup is great because our component code is declarative and the CSS manages the transitions in between our states of mounted and unmounted (e.g. we're not iterating over `opacity` and `y` position).
181+
182+
## Try it out!
183+
184+
Check out the Codepen example:
185+
186+
<iframe height='265' scrolling='no' src='//codepen.io/jackoliver/embed/qNwrrp/?height=265&theme-id=0&default-tab=css,result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/jackoliver/pen/qNwrrp/'>React DailyUI - 001 - Sign Up</a> by Jack Oliver (<a href='http://codepen.io/jackoliver'>@jackoliver</a>) on <a href='http://codepen.io'>CodePen</a>.
187+
</iframe>
188+
189+
> The complete sourcecode for this article is available on github at:
190+
> [get code for this project here](github link to repo path).
191+
>
192+
> To start the app cd into the code and type:
193+
>
194+
> npm install
195+
> npm start
196+
>
197+
198+
## Sign up for email updates for the next release
199+
200+
## Bios / Links here
201+
202+
203+
204+
205+
206+
207+
208+
209+
210+
211+
212+

‎001-sign-up/index.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<link rel="shortcut icon" href="./src/favicon.ico">
7+
<title>React App</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<!--
12+
This HTML file is a template.
13+
If you open it directly in the browser, you will see an empty page.
14+
15+
You can add webfonts, meta tags, or analytics to this file.
16+
The build step will place the bundled scripts into the <body> tag.
17+
18+
To begin the development, run `npm start` in this folder.
19+
To create a production bundle, use `npm run build`.
20+
-->
21+
</body>
22+
</html>

‎001-sign-up/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "001-sign-up",
3+
"version": "0.0.1",
4+
"private": true,
5+
"devDependencies": {
6+
"react-scripts": "0.4.1"
7+
},
8+
"dependencies": {
9+
"react": "^15.3.1",
10+
"react-dom": "^15.3.1"
11+
},
12+
"scripts": {
13+
"start": "react-scripts start",
14+
"build": "react-scripts build",
15+
"test": "react-scripts test --env=jsdom",
16+
"eject": "react-scripts eject"
17+
},
18+
"eslintConfig": {
19+
"extends": "./node_modules/react-scripts/config/eslint.js"
20+
}
21+
}

‎001-sign-up/src/App.css

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
@import "https://fonts.googleapis.com/css?family=Lato:100,300,400,700,900";
2+
body {
3+
background-image: url(https://unsplash.it/1400/795);
4+
background-position: center;
5+
background-repeat: no-repeat;
6+
background-size: cover;
7+
color: #3E363F;
8+
font-family: "Lato", sans-serif;
9+
height: 100vh;
10+
overflow: hidden;
11+
}
12+
13+
.App {
14+
align-items: center;
15+
display: flex;
16+
height: 100vh;
17+
justify-content: center;
18+
width: 100vw;
19+
position: absolute;
20+
top: 0;
21+
left: 0;
22+
}
23+
24+
.Modal {
25+
background: #3E363F;
26+
border-radius: 2px;
27+
padding: 20px;
28+
width: 200px;
29+
}
30+
31+
.Input {
32+
display: flex;
33+
flex-direction: row-reverse;
34+
border-bottom: 1px solid rgba(255, 252, 232, 0.1);
35+
padding-bottom: 3px;
36+
margin-bottom: 5px;
37+
}
38+
.Input input {
39+
outline: none;
40+
border: 0;
41+
color: #FFFCE8;
42+
background: transparent;
43+
font-family: "Lato", sans-serif;
44+
flex: 1 0 auto;
45+
font-size: 14px;
46+
font-weight: 300;
47+
}
48+
.Input input:focus ~ label {
49+
opacity: 1;
50+
}
51+
.Input input[type='text'] ~ label::before {
52+
content: "\f007";
53+
}
54+
.Input input[type='email'] ~ label::before {
55+
content: "\f1fa";
56+
}
57+
.Input input[type='password'] ~ label::before {
58+
content: "\f023";
59+
}
60+
.Input label {
61+
font-family: FontAwesome;
62+
font-size: 13px;
63+
opacity: .1;
64+
transition: opacity .5s ease;
65+
}
66+
.Input label::before {
67+
align-items: center;
68+
color: #FFFCE8;
69+
display: flex;
70+
height: 30px;
71+
justify-content: center;
72+
width: 30px;
73+
}
74+
75+
button {
76+
align-items: center;
77+
background: #DD403A;
78+
border: 0;
79+
border-radius: 3px;
80+
color: white;
81+
display: flex;
82+
font-family: "Lato", sans-serif;
83+
font-size: 13px;
84+
font-weight: 500;
85+
justify-content: center;
86+
margin-top: 20px;
87+
outline: none;
88+
padding: 10px 9px 10px 11px;
89+
text-transform: uppercase;
90+
width: 100%;
91+
}
92+
button .fa {
93+
font-size: 12px;
94+
margin-left: auto;
95+
position: relative;
96+
top: 1px;
97+
}
98+
button:hover {
99+
transform: scale(1.02);
100+
}
101+
button:active {
102+
transform: scale(0.99);
103+
}
104+
105+
.example-enter {
106+
margin-top: 30px;
107+
opacity: .01;
108+
}
109+
.example-enter.example-enter-active {
110+
margin-top: 0px;
111+
opacity: 1;
112+
transition: opacity .5s ease, margin .5s ease;
113+
}
114+
115+
.example-leave {
116+
margin-top: 0px;
117+
opacity: 1;
118+
}
119+
.example-leave.example-leave-active {
120+
margin-top: -30px;
121+
opacity: .01;
122+
transition: opacity .3s ease, margin .5s ease;
123+
}

0 commit comments

Comments
 (0)