Skip to content

Instantly share code, notes, and snippets.

@m0n01d
Last active June 8, 2017 16:35
Show Gist options
  • Save m0n01d/0af68d63a2dfa99d9b6a03969cc65df5 to your computer and use it in GitHub Desktop.
Save m0n01d/0af68d63a2dfa99d9b6a03969cc65df5 to your computer and use it in GitHub Desktop.

Annotations by line

1:

import React from node modules the { Component } bit is like a shorthand for var Component = React.Component;

Remember, React has to be in scope because the code generated after its transpiled by babel relies on React being there React.createElement

3: TodoItem

shorthand

const TodoItem = ({ text, handleClick }) => (
  <li>{text} <button onClick={handleClick}>x</button></li>
);

written longhand is

const TodoItem = function(props) {
  const text = props.text;
  const handleClick = props.handleClick;

  return (
      <li>{text} <button onClick={handleClick}>x</button></li>
  );
}

TodoItem is a "functional component"

it takes in arguments (props) and returns jsx markup

in the shorthand version, i'm using "destructuring" to pick out the text and handleClick properties from props

so essnetially props is an object like:

const props = {
  text: 'drink a beer',
  handleClick: () => {}, // some function
}

const { text, handleClick } = props;

// compare that to
const text = props.text;
const handleClick = props.handleClick;

// we're pulling out the text and handleClick properties off an object

you can do that with any object.

7:

we're using ES6 class syntax to create a React Component We're using class because this component will have state its not a functional component like our TodoItem link to classes

8:

constructor passes in props, just like our functional component above but we have to tell React to do something with those props so it sets up all the magic react gives to us, so we have to call super(props) here were dealing with class inheritance (look this up, theres a lot there)

11:

here were setting our initial state our state is an object with a todos property, which is defaulted with some todos

15:

addTodo is a method we call when we want to add a todo

our <input onKeyUp={this.addTodo.bind(this)} /> receives this method as the keyup handler so whenever we type in the input, this method gets invoked line 25, it checks if the key pressed is the Enter key if so, we read the inputs value through e.target.value make a new todo item out of it, then add it to our state

we have to use .bind(this) to it because otherwise the this value inside addTodo would point to the input itself, just like youd expect while using jQuery

$('input').on('keyup', function(e) { var text = $(this).val(); // this refers to the input })

20:

we're using the setState method, setState comes from the React.Component that we're extending

it lets our React component App know that we changed its state, and it should re-render

setState takes an object, with the property we want to change/set, in this case we want to update our todos array on our state

so we're using the spread operator to take all the values currently in our state, and carry them over to our new state

  const todo = e.target.value;
  this.setState({
    todos: [todo, ...this.state.todos],
  });

  e.target.value = '';

this.state.todos is now a brand new array, with our newly added todo item, along with all the values in our previous state, by spreading them over our new array. essentially concatenating our old state with our new state and then we reset our input tag back to an empty input

28:

removeTodo(i)

here we're setting up an event handler to remove a todo item by its index again we're using setState to set our todos array

we're making use of the .slice() method in 2 places. the .slice() method creates a copy of an array,

so we want to first grab all the todos up until, but not including, the index of our todo item in our todos array, and we're using the ...spread operator again, to grab all the values in our new array, spread them out into our new array,

and then again for the second half of our todos, we use todos.slice(i + 1, this.state.todos.length), to skip over i, i + 1, and then slice it until the end of the array. essentailly 3 arrays are being created

this.setState({
    todos: [ // <-- first, array literal that will be our new todos array
      //
    ],
  })

  [
    ...this.state.todos.slice(0, i), // <-- second array
    ...this.state.todos.slice(i + 1, this.state.todos.length), <-- third array
  ]

36:

render() heres where the magic happens react takes in the state, and renders our view most of the magic is going on in the <ul>

we're making use of the array method .map

.map loops over the array, in this case todos and applies a function to each item in the array in turn much like you would do with a for loop

written long hand it is

  {this.state.todos.map(function(todo, i) {
    return (
      <TodoItem
        key={i}
        text={todo}
        handleClick={() => this.removeTodo(i)}
      />
    )}
  }

the return value of the function gets added to a new array

and example

function square(n) {
  return n * n;
}

function double(n) {
  return n * 2;
}

var nums = [1, 2, 3];

var squaredNums = nums.map(square);
var doubledNums = nums.map(double);

console.log(squaredNums); // 1, 4, 9
console.log(doubledNums); // 2, 4, 6

written with for loops

let squaredNums = [];
for (var i = 0; i < nums.length; i++) {
  const num = nums[i];
  const squared = square(num);
  squared.push(squared);
  // or simply
  squared.push(square(nums[i]));
}

the .map method maps an input to an output double(5) => 10 square(12) => 144

the .map passes 2 arguments to our function, the item in the array, and the index of that item in the array much like a for loop for (var i = 0; i < todos.length; i++){}

so in our app were mapping over our state, taking our todos as input, and outputting a list of <TodoItems />

the key={i} property on out <TodoItem> helps react keep track of the items its rendered on the page. so when our state updates, for instance by adding a todo, it wont have to rerender our whole list item, it can safely inject a new todo item into our list. and then when we delete an item it can remove that item from our ul without affecting any other element on our page

and then for our TodoItem, for it to be able to remove the todo from our state, we supply the handleClick property our TodoItem expects to bind to its button, we supply it with the i to it knows which index to remove from our todos and we give it a text property which is the todo item in our array so that it can render it

import React, { Component } from 'react';
const TodoItem = ({ text, handleClick }) => (
<li>{text} <button onClick={handleClick}>x</button></li>
);
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: ['make a todo app', 'drink a beer', 'take a nap'],
};
}
addTodo(e) {
if (e.which == 13) {
// we pressed enter
const todo = e.target.value;
this.setState({
// todos: this.state.todos.concat(todo),
todos: [todo, ...this.state.todos],
});
e.target.value = '';
}
}
removeTodo(i) {
this.setState({
todos: [
// ...this.state.todos.slice(0, i),
// ...this.state.todos.slice(i + 1, this.state.todos.length),
],
})
}
render() {
return (
<div className="App">
<input
type="text"
placeholder="add a new todo"
onKeyUp={this.addTodo.bind(this)}
/>
<ul>
{this.state.todos.map((todo, i) => (
<TodoItem
key={i}
text={todo}
handleClick={() => this.removeTodo(i)}
/>
))}
</ul>
</div>
);
}
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment