diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..24f49f2
Binary files /dev/null and b/.DS_Store differ
diff --git a/readme.md b/README.md
similarity index 81%
rename from readme.md
rename to README.md
index 8c525b3..6802925 100644
--- a/readme.md
+++ b/README.md
@@ -7,8 +7,8 @@
- Pass state from parent components to children as props
- Pass state from children components to their parents as arguments to functions
-## Framing (0:10 / 0:10)
-For today, we'll be creating a Todo app in React. Before we start coding it, we're going to do a code review of a simple todo application. Then, we'll build the application.
+## Framing
+For today, we'll be creating a Todo app in React.
We've learned a tremendous amount about object oriented structures for web development. And they were great. With angular, we dabbled a bit with feature-based separation of concerns. React's component model takes that separation further and reduces the potential of tight coupling that often attends object oriented. Think of the FIRST principles:
@@ -36,68 +36,25 @@ Ideally, components should be short and condensed.
Because the same input will always produce the same output, components are easily unit testable.
-
-## You do - Checkout React Todo (0:05 / 0:15)
-Before we can checkout the react todo app, we need to grab up our backend that will serve up our todos:
-
-```bash
-$ git clone git@github.com:ga-wdi-exercises/react-todo-api.git
-$ cd react-todo-api
-$ bundle exec rake db:create
-$ bundle exec rake db:migrate
-$ bundle exec rake db:seed
-$ rails s -p 4000
-```
-
-> This api needs to be served on port 4000 because when we run our react application it defaults to port 3000.
-
-Now lets grab the react todo app:
-
-```bash
-$ git clone git@github.com:ga-wdi-exercises/react-todo.git
-$ npm install
-$ npm start
-```
-
-Now go to `http://localhost:3000` and play with the site!
-
-After we've played with the site for a bit, take a look at the code base. Start at `src/index.js`. In that file, we can see that it uses another file `config/routes.js`. Don't worry too much about understanding all the specific syntax just get a good overview of the code base.
-
-### Code Review in Comments - Pair Up! (0:15 / 0:30)
-
-Pair up!
-
-As you both progress from one file to another and scan through the code base, write down in-line comments of what you think that particular piece of code is doing. You don't have to write a comment for every line, but at least one for each function/method
-
-Some things you should consider/do during code review:
-
-- How is the application CRUD'ing the data?
-- What is the application's point of entry?
-- Are any components reused? Which one/ones?
-- How is state being passed from parent components to their children?
-- How is state being passed from child component to it parents? (*this is tough!*)
-- Make use of a `console.log()` if you are not sure what data some variable is holding, or when a method is executing.
-
-> while changing the app, if the code breaks, just do a `$ git checkout -- .` to get back to the original code base
-
## React Todo
Alright it's time to build! We're going to be building this application from scratch! It won't be exactly like the repo above, but it'll be pretty close and follow much of the same structure.
-> If you get behind, all code written today will be in the lesson plan. Additionally the repo you code reviews contains most of the same code. So please keep questions pertinent to content rather then debugging specific errors you may be getting. Should also note that some of the code snippets will be repetitions to reiterate points of learning. Some of them might just be updates to existing files. Some of them might be brand new content you have to add all of.
+> If you get behind, all code written today will be in the lesson plan. The error messages you'll get in terminal and in the chrome dev tools from React are usually very accurate and helpful, so please utilize them. Please keep questions pertinent to content. We should also note that some of the code snippets will be repetitions to reiterate points of learning. Some of them might just be updates to existing files. Some of them might be brand new content you have to add all of.
### Getting Started
-Close down the react app you have running currently. Next, let's create the react app.
+Now let's create the react app. We're going to be using react-router v3 for this, so we will need to install that as well.
```bash
$ create-react-app my-react-todo
$ cd my-react-todo
+$ npm install react-router@3.0.0
$ npm start
```
Now, if we navigate to [`localhost:3000`](http://localhost:3000) we will see the boilerplate create-react-app React application.
-### First Step - Hello World (0:10 / 0:40)
+### First Step - Hello World
#### Get rid of things we won't use
@@ -131,7 +88,7 @@ export default App;
> Hooray for automatic rerendering on save! If we just switch over to our browser we'll automatically see our updates.
-### React Router (0:45 / 0:55)
+### React Router
We're going to use React Router today to introduce it as a concept. However, it isn't strictly necessary for this application. We're really just going for exposure here. There's a lot to learn about react router and we'll just be scratching the surface. If you want to dive deeper, checkout [this tutorial](https://github.com/reactjs/react-router-tutorial)
We need React Router in the same way that we needed angular routers. We need a way to link to various urls to components in our application. Because our application will be a SPA, we still want to preserve different application-states via the url. This Todo app's application-states (not to be confused with component state) will just be the root url and a url to all todos(`/` and `/todos`)
@@ -140,7 +97,6 @@ We need React Router in the same way that we needed angular routers. We need a w
It's great, Routes are just react Components as well! Let's start by installing the `react-router` dependency, making a `config` folder and a `routes.js` file that will contain our routes:
```bash
-$ npm install react-router --save
$ mkdir src/config
$ touch src/config/routes.js
```
@@ -159,7 +115,7 @@ module.exports = (
All we've done here is added some dependencies as well as added our App component to this file. Then we used the `Route` component, given to us by `react-router` to create a route for the root path(`'/'`). We also establish that the component that should be rendered here is the App component we defined earlier.
-> Something that's weird is that we imported `React` from `'react'` but then we imported `{Route}` from `'react-router'`. What's with the curly braces? In the latter case we're actually only importing a specific module of the `react-router` and name spacing it within `Route` If we had omitted the curly's it would have grabbed all of `react-router` functionality. Check out the [react router source code](https://github.com/reactjs/react-router/tree/master/modules) and we can clearly see the Route is a module within react-router
+> Something that's weird is that we imported `React` from `'react'` but then we imported `{Route}` from `'react-router-dom'`. What's with the curly braces? In the latter case we're actually only importing a specific module of the `react-router-dom` and name spacing it within `Route` If we had omitted the curly's it would have grabbed all of `react-router-dom` functionality. Check out the [react router source code](https://github.com/reactjs/react-router-dom) and we can clearly see the Route is a module within react-router-dom
Great, we've defined out routes, but it's not going to do anything because nothing knows about this file yet. Let's update our `index.js` to use a Router now instead of just rendering the `App` Component. In `index.js`:
@@ -177,30 +133,25 @@ ReactDOM.render(
);
```
-In this file, we're using the `Router` component to specify what react should render. We pass in some properties to this `Router` component: `routes`, the file we just defined prior, and `browserHistory`.
-
-We've just gone over `routes.js` but not `browserHistory`.
-
-In a nutshell, a history knows how to listen to the browser's address bar for changes and parses the URL into a location object that the router can use to match routes and render the correct set of components.
-
Great, we should now be able to see hello world show up!
-## Break (0:10 / 1:05)
-### A Simple Component (0:05 / 1:10)
+
+### A Simple Component
Before we add another route, let's change the header to be more applicable and make it its own component.
In `src/App.js`:
```js
import React, { Component } from 'react';
-import Header from './components/Header.js'
+import Header from './components/Header'
class App extends Component {
render() {
return (
-
+
Hello, and welcome! I am a heading tag in App.js! Have a great day!
+
);
}
@@ -247,7 +198,7 @@ Warning: [react-router] Location "/todos" did not match any routes
This warning makes sense, our `config/routes.js` only has a reference to `'/'` and nothing else. We'll fix that by adding the first parts of our app's main functionality. But before that... let's talk about containers.
-### Containers (0:20 / 1:30)
+### Containers
As we first start to write this container, its going to seem like just another component. Remember that React components should be FIRST: focused, independent, reusable, small, and testable. In order to help keep components slim, a good practice is to move as much of the business logic surrounding a component's state to a container component. We're going to put all that logic in this container. It will start out very similarly to our `Header` component, but end up much more complex.
Let's start by creating a containers folder and then the container file:
@@ -285,7 +236,7 @@ module.exports = (
-)
+);
```
If we click on it we should totally see ..... nothing still. But no error now! Because our `/todos` is nested within our `'/'` route, our `App` Component needs to know what to render. We do this by adding one line of code to our `src/App.js`:
@@ -307,7 +258,7 @@ Great everything works!
Everything up to this point, is most of what you need to know about using react for a website NOT using a back end. [Just add css through index.css and you're good to go!](https://gist.github.com/superbuggy/29693beaa19cbc2a9171aba4f373dc32)
-### Fetching Data (0:30 / 2:00)
+### Fetching Data
React actually isn't as full featured as say AngularJS or BackboneJS. It relies on third party libraries to fetch data. Today, we'll be using a library called [Axios](https://github.com/mzabriskie/axios), a promise based HTTP client for the browser and node. Let's install the module now and also create the folder/file that will contain our database logic:
@@ -317,14 +268,14 @@ $ mkdir src/models
$ touch src/models/Todo.js
```
-Now in `src/models/Todo.js`:
+Now in `src/models/Todo.js`, we are going to use our beloved super-crud API endpoint of todos to get some data (you can check out the raw json at https://super-crud.herokuapp.com/todos):
```js
import axios from 'axios'
class TodoModel {
static all(){
- let request = axios.get("http://localhost:4000/todos")
+ let request = axios.get("https://super-crud.herokuapp.com/todos")
return request
}
}
@@ -340,11 +291,6 @@ Note also that `all()` is a static method. What does this mean? A static method
let todos = TodoModel.all()
```
-Does this type syntax look familiar at `.all`? If not, think back to Rails, where in a Todo app, we call the **class method** `.all`:
-
-```rb
-@todos = Todo.all
-```
**Class methods** don't require an instance of the class in order to be called, but an **instance method** does. [More on Static Methods in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Static_methods)
@@ -353,6 +299,7 @@ We can't really test out the code in this file in isolation, so we must `import`
For now, let's toss this in the `TodosContainer`'s `render()` method: this isn't ultimately going to be where we want to call `TodoModel.all()`, but for testing purposes, it will suffice.
In `components/TodosContainer.js`:
+
```js
import React, {Component} from 'react'
import TodoModel from '../models/Todo'
@@ -376,8 +323,8 @@ export default TodosContainer
Awesome, we can see the response from our database as soon as the page loads, we know it's working! However, its completely in the wrong spot and we don't have anything we're passing todos to... yet!
Now that we can get our data, let's code how we present that data. It'll be a bit before we connect these pieces and actually see our todos in our app, but just hold on we'll get there!
-## Break (0:10 / 2:10)
-### Rendering A Todo (0:20 / 2:30)
+
+### Rendering A Todo
Let's start at the bottom and bubble up. It'll be nice if each todo we're its own component. To follow FIRST(Focused Independent Reusable Small Testable). Let's create `src/components/Todo.js` and put the following in it:
```js
@@ -410,7 +357,7 @@ class Todos extends Component {
let todos = this.props.todos.map( (todo) => {
return (
)
})
@@ -449,7 +396,7 @@ class TodosContainer extends Component {
fetchData(){
TodoModel.all().then( (res) => {
this.setState ({
- todos: res.data,
+ todos: res.data.todo,
todo: ''
})
})
@@ -514,12 +461,14 @@ If we take a look at the `props` being passed from one component to the next, we
In `src/containers/TodosContainer.js`:
-```js
+
+```javascript
```
-In `src/components/Todos.js`:
+In `src/components/Todos.js`:
+
```js
let todos = this.props.todos.map( (todo) => {
return (
@@ -534,7 +483,7 @@ In `src/components/Todos.js`:
In `src/components/Todo.js`:
```js
-
+
{this.props.todo.body}
```
@@ -651,18 +600,23 @@ First off, prevent the default action as form submission will cause a request to
It needs to be supplied from its parent component. Let's update the `src/containers/TodosContainer.js` so that we can successfully create todos:
-In `src/containers/TodosContainer.js`:
+In `src/containers/TodosContainer.js`:
+
```js
// At the top import the component
import CreateTodoForm from '../components/CreateTodoForm'
// adding rest of code to container, more code above
-createTodo(todo){
- let newTodo = {body: todo, completed: false}
- TodoModel.create(newTodo).then( (res) => {
- let todos = res.data
- this.setState({todos})
- })
+createTodo(todo) {
+ let newTodo = {
+ body: todo,
+ completed: false
+ }
+ TodoModel.create(newTodo).then((res) => {
+ let todos = this.state.todos
+ let newTodos = todos.push(res.data)
+ this.setState({newTodos})
+ })
}
render(){
return (
@@ -683,7 +637,7 @@ In the actual `createTodo` function. We can see that we construct everything we
```js
static create(todo) {
- let request = axios.post("http://localhost:4000/todos", todo)
+ let request = axios.post("https://super-crud.herokuapp.com/todos", todo)
return request
}
```
@@ -752,7 +706,7 @@ We've added a span with an `X` in it. When it gets clicked it invokes the `onDel
let todos = this.props.todos.map( (todo) => {
return (
)
@@ -762,11 +716,13 @@ let todos = this.props.todos.map( (todo) => {
Looks like it's not defined here either but passed yet again from a parent container. Finally in the `src/components/TodosContainer.js`:
```js
-deleteTodo(todo){
- TodoModel.delete(todo).then( (res)=>{
- let todos = res.data
- this.setState({todos})
- })
+deleteTodo(todo) {
+ TodoModel.delete(todo).then((res) => {
+ let todos = this.state.todos.filter(function(todo) {
+ return todo._id !== res.data._id
+ });
+ this.setState({todos})
+ })
}
render(){
return (
@@ -786,7 +742,7 @@ Before we talk about the above code, lets look at what delete looks like in our
```js
static delete(todo){
- let request = axios.delete(`http://localhost:4000/todos/${todo.id}`)
+ let request = axios.delete(`https://super-crud.herokuapp.com/todos/${todo._id}`)
return request
}
```
@@ -798,12 +754,22 @@ The `deleteTodo` takes the todo, passed from the child Component of `Todo` up th
### Implementing Edit
In `containers/TodosContainer.js`:
-
```js
+updateTodo(todoBody) {
+ var todoId = this.state.editingTodoId
+ function isUpdatedTodo(todo) {
+ return todo._id === todoId;
+ }
+ TodoModel.update(todoId, todoBody).then((res) => {
+ let todos = this.state.todos
+ todos.find(isUpdatedTodo).body = todoBody
+ this.setState({todos: todos, editingTodoId: null, editing: null})
+ })
+}
editTodo(todo){
this.setState({
- editingTodoId: todo.id
+ editingTodoId: todo._id
})
}
render(){
@@ -823,36 +789,33 @@ render(){
```
Why would we add editingTodoId to the container? Why might the container be aware of a ***single*** todo ID, in the context of an edit?
-
In the `components/Todos.js`, add `editingTodoId` and `onEditTodo` to `` props:
```js
+//....
let todos = this.props.todos.map( (todo) => {
return (
)
})
+//...
```
-In `components/Todo.js`
+In `components/Todo.js` We need to use the method:
```js
render(){
- if (this.props.editingTodoId === this.props.todo.id){
- //if we see this console.log, we know that Todo-props are being
- // passed into TodosContainer, and being set as the
- // TodosContainer-state, and then trickling down as props to
- // the Todo component. WHATttttt argh
- // this is broken down below
+ if (this.props.editingTodoId === this.props.todo._id){
console.log(`${this.props.todo.body} is being edited`);
}
return(
@@ -923,37 +886,94 @@ return (
)
```
-You will then have to both write that component and then import it into `components/Todo.js`. Refer to the file-tree in [the example here](https://github.com/ga-wdi-exercises/react-todo/tree/master/src).
+You will then have to both write that component and then import it into `components/Todo.js`:
-### Getting Started with implementing update:
+```js
-In `models/Todo.js` add:
+//TodoForm.js
+import React, {Component} from 'react'
-```js
-static update(todo){
- let request = axios.put(`http://localhost:4000/todos/${todo.id}`, {body: todo.body})
- return request
+class TodoForm extends Component {
+ onChange(event) {
+ this.setState({
+ todo: event.target.value
+ })
+ }
+ onSubmit(event){
+ event.preventDefault()
+ var todo = this.state.todo
+ console.log("todo is", todo)
+ this.props.onUpdateTodo(todo)
+ this.setState({
+ todo: ""
+ })
+ }
+ render(){
+ return (
+
+
+
+ )
+ }
}
-```
-
-Think back to what we did for the other CRUD actions--we define some axios behavior in `/models/Todo.js`. Then we define a method in `TodosContainer` that will handle update behavior.
-Then we make our way down from `TodosContainer` to `Todos` to `Todo`, with `state` trickling down as `props`.
+export default TodoForm
-Refer again to [the example here](https://github.com/ga-wdi-exercises/react-todo/tree/master/src).
+```
-#### Reusability
+```js
+//Todo.js
+//...
+console.log(`${this.props.todo.body} is being edited`);
+return (
+
+)
+//...
+```
-Just like we used partials in Rails for forms, we have this same ability in React.
+```js
+//Todos.js
+let todos = this.props.todos.map( (todo) => {
+ return (
+
+ )
+})
+//...
+```
-[General Todo Form](https://github.com/ga-wdi-exercises/react-todo/blob/master/src/components/TodoForm.js)
+In `models/Todo.js` add our method:
-[Create Todo Form](https://github.com/ga-wdi-exercises/react-todo/blob/master/src/components/CreateTodoForm.js)
+```js
+static update(todoId, todoBody) {
+ let request = axios.put(`https://super-crud.herokuapp.com/todos/${todoId}`, {
+ body: todoBody
+ })
+ return request
+}
+```
-Check out how a TodoForm is composed within the CreateTodoForm!
+Think back to what we did for the other CRUD actions--we define some axios behavior in `/models/Todo.js`. Then we define a method in `TodosContainer` that will handle update behavior.
-## Updating Completion Status
+Then we make our way down from `TodosContainer` to `Todos` to `Todo`, with `state` trickling down as `props`.
-You'll just need to make a simple modification to your `fetchData()` method to sort the to-dos.
+## Conclusion
-You'll then need to add UI: a button or some element with an `onClick` that calls a function in `TodosContainer` that **toggles** completeness.
+We've learned how to do full CRUD for a basic todo app here. We've seen in particular how props can be trickled down through parent and child components to make a very modular app. We've also been introduced to the magic of axios for network calls from our frontend.
diff --git a/my-react-todo/.gitignore b/my-react-todo/.gitignore
new file mode 100644
index 0000000..927d17b
--- /dev/null
+++ b/my-react-todo/.gitignore
@@ -0,0 +1,18 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
diff --git a/my-react-todo/README.md b/my-react-todo/README.md
new file mode 100644
index 0000000..892ea8f
--- /dev/null
+++ b/my-react-todo/README.md
@@ -0,0 +1 @@
+This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
diff --git a/my-react-todo/package.json b/my-react-todo/package.json
new file mode 100644
index 0000000..038431a
--- /dev/null
+++ b/my-react-todo/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "my-react-todo",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "axios": "^0.16.0",
+ "react": "^15.4.2",
+ "react-dom": "^15.4.2",
+ "react-router": "^3.0.0"
+ },
+ "devDependencies": {
+ "react-scripts": "0.9.5"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
diff --git a/my-react-todo/public/favicon.ico b/my-react-todo/public/favicon.ico
new file mode 100644
index 0000000..5c125de
Binary files /dev/null and b/my-react-todo/public/favicon.ico differ
diff --git a/my-react-todo/public/index.html b/my-react-todo/public/index.html
new file mode 100644
index 0000000..aab5e3b
--- /dev/null
+++ b/my-react-todo/public/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ React App
+
+
+
+
+
+
diff --git a/my-react-todo/src/App.js b/my-react-todo/src/App.js
new file mode 100644
index 0000000..d516ba9
--- /dev/null
+++ b/my-react-todo/src/App.js
@@ -0,0 +1,15 @@
+import React, { Component } from 'react';
+import Header from './components/Header'
+
+class App extends Component {
+ render() {
+ return (
+
+
+ )
+ }
+}
+
+export default Header
diff --git a/my-react-todo/src/components/Todo.js b/my-react-todo/src/components/Todo.js
new file mode 100644
index 0000000..24058c8
--- /dev/null
+++ b/my-react-todo/src/components/Todo.js
@@ -0,0 +1,34 @@
+import React, {Component} from 'react'
+import TodoForm from '../components/TodoForm'
+
+class Todo extends Component {
+ render(){
+ if (this.props.editingTodoId === this.props.todo._id){
+ //if we see this console.log, we know that Todo-props are being
+ // passed into TodosContainer, and being set as the
+ // TodosContainer-state, and then trickling down as props to
+ // the Todo component.
+ console.log(`${this.props.todo.body} is being edited`);
+ return (
+
+ )
+ }
+ return(
+