<style>
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap');
.reveal {
font-family: "Roboto Mono", monospace;
font-size: 40px;
}
</style>
# MERN - Putting it Together
### Open Source @ UCSD
###### tags: `MERN`
---
## Event Handlers
----
Run some code whenever an event (such as a button click or a user types) occurs!
----
### Adding Button Click Handlers
```javascript=
onClick = (e) => {
/*
* e is the click event object
* Holds information about the click itself
* and about who was clicked
*/
console.log(e);
}
```
``` javascript=
<button onClick={onClick}>Submit</button>
```
----
### Adding "Change" Handlers (Typing)
```javascript=
class Example extends React.Component{
constructor(){
super();
this.state = {
name: '',
email: '',
bio: ''
}
}
onChange = (e) => {
// sets the state variable with the name of evt.target.id(name or email)
this.setState({[e.target.id]: e.target.value})
}
render(){
// note that the id's match the state variable associated with the input
return(
<form onChange={onChange}>
<label>Name: </label>
<input type="text" id="name"><br/>
<label>Email: </label>
<input type="email" id="email"><br/>
<label>Bio: </label>
<input type="text" id="bio"><br/>
<button type="submit">Create User</button>
</form>
)
}
}
```
----
```javascript=
class CreateUser extends React.Component{
constructor(){
super();
this.state = {
name:'',
bio:'',
email:'',
}
}
onChange = evt => {
this.setState({[evt.target.id]:evt.target.value})
}
onClick = evt => {
evt.preventDefault();
const newUser = {
name: this.state.name,
bio: this.state.bio,
email: this.state.email,
}
// somehow submit this new user to database
console.log(evt);
}
render(){
<Form onChange={this.onChange} onSubmit={this.onSubmit}> </Form>
}
}
```
----
### Your Turn!
Add event handlers for Create Tweet!
1. onChange handler for recording the user inputs
2. onSubmit handler for sending the data to the endpoint
- console log the user inputs is sufficient for now
---
## Where and When to Query the Database
----
## GET all tweets when the page renders
We will be using something called **lifecycle methods**.
Lifecycle methods are methods automatically called by React (if you define them) when a certain event in a component's lifecycle happens
----
## The phases of a component life
https://www.w3schools.com/react/react_lifecycle.asp
1. Mounting
2. Updating
3. Unmounting
----
## Mounting
When elements are being put into the DOM
Methods that get called during mounting:
1. constructor()
2. getDerivedStateFromProps()
3. render()
4. componentDidMount()
----
## Updating
When a component is updated (usually by changing the state)
Methods that get called during updating:
1. getDerivedStateFromProps()
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate()
5. componentDidUpdate()
----
## Unmounting
When a component is removed from the DOM
Methods that get called during unmounting:
1. componentWillUnmount()
----
* For example, we will want to GET all tweets when the TweetsList component renders
* We will be using the componentDidMount() lifecycle method for this query
* componentDidMount() is ran after your component is rendered for the **first** time
----
Where do we want to add the new user to the database?
----
In the submit button click handler in CreateUser!
----
How about when we create a new Tweet? Where will we add that to the database?
----
In the submit button click handler in CreateTweet!
----
Now let's actually make these requests
---
## HTTP Request
----
## That's so fetch!
We will be using the `fetch` web API in order to handle HTTP requests and responses
----
## Example Usage
```javascript=
fetch('/users/')
.then((resp) => {
// need to parse the response object (extract the JSON)
return resp.json();
})
.then((data) => {
// now you have the data!
console.log(data);
})
```
----
## Making a POST request with fetch
```javascript=
fetch('/users/add', {
// send as a POST request with the new user
method: 'POST',
body: JSON.stringify(newUser)
})
.then((resp) => resp.json())
// if success and data was sent back, log the data
.then((data) => console.log("Success", data))
// if failure, log the error
.catch((err) => console.log("Error", err));
```
---
## Promises
What's up with .then()!?
----
HTTP requests are slow boiz because communicating with the server takes time.
The next line after an HTTP request will be ran before the request is finished :(
This can be bad if you're using the data from the request in the next line (such as mapping the data to the screen)
----
This is where Promises come to save us!
Making a request using fetch does NOT return the actual data itself; it returns something called a Promise
----
## What is a Promise?
* An object that represents the eventual completion (or failure) of some asynchronous task (such as an HTTP request)
* How can you access the data from a Promise? Use .then()!
```javascript=
// this fetch returns a promise, and .then() lets us obtain the data returned!
fetch('/users/add', {
method: 'POST',
body: JSON.stringify(newUser)
})
// the data returned from the fetch is a response object, so we have to parse it
.then((resp) => resp.json())
.then((data) => console.log("Success", data))
.catch((err) => console.log("Error", err));
```
----
Note that we have two .then()'s because extracting data from respose data is ALSO asynchronous (a.k.a. takes some time)
---
## GET all users in componentDidMount()
We want to fetch all users from the database and store the data in a state variable so we can access and use it later.
Live coding!
----
```javascript=
constructor(){
super();
this.state = {
users: []
}
}
componentDidMount(){
fetch("/users/")
.then(resp => resp.json())
.then(users => this.setState({users}))
}
```
---
## POST a new user in CreateUser
Live coding!
----
```javascript=
onSubmit = evt => {
evt.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
bio: this.state.bio
}
fetch("/users/add", {
method: "POST",
body: JSON.stringify(this.state.newUser)
})
.then(resp => resp.json())
.then(data => console.log("Success!", data))
.catch(err => console.log("Failure", err));
}
```
---
## Your turn!
1. Implement the GET all tweets request to fetch tweets from the database
- store it in a state variable in the View All Tweets page
3. Implement the CreateTweet fetch request to add a tweet to the database!
---
## Displaying Data to the Front-end
Using the data fetched from the HTTP requests you just make, let's now display those data to our app!
----
## Displaying users when creating tweets
In the Create Tweet page, instead of having a text input for the user field, we can instead have a dropdown menu of all of the users that we have stored in the database. To do this, we'll need to use `.map()` (recall the Javascript workshop)
----
``` javascript
<Form>
<Form.Group>
<Form.Label>Author:</Form.Label>
<Form.Control as="select">
{this.state.users.map(user => {
return(
<option key={user} value={user}>
{user}
</option>
)
})}
</Form.Control>
</Form.Group>
```
----
## Storing data in the state!
We want to store which author was selected in the state by using the onChange attribute!
```javascript=
constructor(){
super();
this.state = {
author:''
}
}
onChange = evt => {
this.setState({[evt.target.id]:evt.target.value})
}
// somewhere inside of your form
<Form.Control as="select" onChange={this.onChange}>
```
----
## Displaying tweets to the front page
### Your Turn!
You should have already fetched the data from the database into a state variable. Now, display all tweets on the View All Tweets page (tweet-list.js).
---
## Testing everything
We should have a working Tweeter app with basic functionalities:
1. Create a new user
- Make a new user with the name, email, and bio
- All users will be an option for the author field when creating a tweet
2. Create a new tweet
- Make a new tweet with the tweet text body and author
3. View all tweets
- Display all tweets
---
## Debugging 🐛
----
## Allow access to database from any IP address
1. Go to your MongoDB Atlas cluster
2. Go to Network Settings
3. Click 'Add IP Address'
4. Choose 'Allow Access from Anywhere'
----
## If you're having server errors 😢
You may be getting an error because you're running the server and the client on the same port (3000) :cry:
----
Fix this by going to backend/index.js and changing the port that the server is listening to to 5000 instead of 3000
Then go to all of your fetch requests and replace ```"/users/add"``` with ```"http://localhost:5000/users/add"```
Restart the server and try again!
----
## CORS error 😢
In the backend directory, run `npm install cors`.
In backend/index.js, add
```
const cors = require('cors');
```
After `const app = express()`, add
```
app.use(cors());
```
----
Fix this by going to backend/index.js and changing the port that the server is listening to to 5000 instead of 3000
Then go to all of your fetch requests and replace ```"/users/add"``` with ```"http://localhost:5000/users/add"```
Restart the server and try again!
---
## Congratulations, you've created your own MERN application!