Skip to content

Instantly share code, notes, and snippets.

@kraigh
Last active April 18, 2019 14:03
Show Gist options
  • Save kraigh/fb524c6db6cce065c2ba344724d3c993 to your computer and use it in GitHub Desktop.
Save kraigh/fb524c6db6cce065c2ba344724d3c993 to your computer and use it in GitHub Desktop.
Todo React Snippets

Description

In this file you will find some code snippets and examples of how to do certain things in React.

Component Constructor Method

In the constructor for your component, you perform several important actions.

  • Calling super() or super(props) will call the parent class's constructor and is important for React.
    • Call super(props) if your component has props, and should also be inside constructor(props) in this case.
class App extends Component {
  constructor() {
    super()
    // stuff here
  }
}
class Todo extends Component {
  constructor(props) {
    super(props)
    // stuff here
  }
}
  • Setting up state with any initial values, may be empty. Do this inside the constructor. Should be an object.
this.state = {
  todos: [],
  somethingElse: ''
}
  • Add bindings for any event handlers. For instance, if you have a addTodo method that gets called by an onSubmit on your form, add the binding in the constructor like this:
this.addTodo = this.addTodo.bind(this);

Modifying State

  • Anywhere other than the constructor() method, you modify state using this.setState(), which receives an object of the new state.
  • You can use the ES6 spread operator to pass in existing state data and add to it.
this.setState({
  todos: [...this.state.todos, newTodo]
)};

Event Handling

  • To handle an event such as a form submit, click, or form input change, first create a method in your component class. The event should receive one parameter, called "event".
addTodo(event) {
  // stuff here
}
  • For some events, such as form submits, you will need to prevent the default action by the browser.
event.preventDefault();
  • You have access to the event, component state, as well as any props on the component.
console.log(event.target);
console.log(this.props.id);
console.log(this.state.todos);
<button onClick={this.addTodo}>Add Todo</button>
// OR
<form onSubmit={this.addTodo}>Add Todo ... </form>

Events As Props

  • In our Todo application, the list of todos is saved in this.state.todos in our container component which lives in App.js. Our form for creating a new Todo exists in our NewTodo component inside of NewTodo.js. Because state is encapsulated and only available in the component that creates it, our NewTodo component cannot access the "global" list of Todos stored in this.state.todos inside of App.js.
  • This is a problem when we write our event handler for submitting our NewTodo form and we need to save the new Todo in the state.
  • The solution is to write our event handlers that need to access state in App.js and then pass them to the NewTodo component as props. Then, in the NewTodo component we attach the event handler method prop to the event, such as onSubmit.
  • This is a common problem in React and is one use case for a global state manager like Redux.
  • The React solution we've implemented above is referred to as "Lifting State" and can be read more about here and here.
// App.js
constructor() {
  super();
  this.state = { todos: [] };
  // Dont forget this line for every event handler!
  this.addTodo = this.addTodo.bind(this);
}
addTodo(event) {
  // Handle new Todo form submit
  // Ajax Call
  this.setState({...this.state.todos, newTodo});
}
render() {
  <NewTodo addTodo={this.addTodo} other={stuff} goes={here} />
}
// NewTodo.js
render() {
  <form onSubmit={this.props.addTodo}>
  </form>
}
  • To recap, we've created our addTodo method inside our App component and passed to the NewTodo component as a prop. Now inside of our NewTodo component we can add the method to our onSubmit using this.props.addTodo.

Form Inputs

  • In React, any user data should be saved in state and kept in sync with state. You can do this by setting the value of an input to a property saved in state.
    • Since this state is saved in the App component, you'll need to pass it to your NewTodo component as a prop.
  • You will need to use the onChange event to update state when the input is changed.
    • For a better understanding of this, see the section above on events as props, but for the assignment you will want to have your onChange event in your App component, and pass it to your NewTodo component as a prop.
    • React Form documentation here
// In App.js
onChange(event) {
  // Set the state to the value of the input
  this.setState({
    input: event.target.value
  });
}
render() {
  <NewTodo newTodo={this.newTodo} onChange={this.onChange} input={this.state.input} />
}
// In NewTodo.js
<input value={this.props.input} onChange={this.props.onChange} />
  • Then when the form is submitted, you can read the input value directly from state.
  • You can also clear out the form input value by updating this.state.input
// In App.js
addTodo(event) {
  // read the input value from state
  const newTodoText = this.state.input;
  // Do AJAX
  // Inside your AJAX success
  this.state.input = '';
}

Issues with this

  • Some javascript functions will override the this variable with their own version of it. This is an issue in React because this is how we access this.state and this.props as well as other methods we may have created.
  • To solve this, when you are using a function or method that will override this, save this to a new variable, typically called self. You can then access the props, state, etc like self.state or self.props.
  • A common place to run into this is when using XMLHttpRequest for AJAX calls, the onreadystatechange() method overrites this.
var self = this;
var createRequest = new XMLHttpRequest();
createRequest.onreadystatechange = function () {
  if (this.readyState === 4 && this.status === 200) {
    // save new Todo to state
    self.setState({
      todos: [...self.state.todos, JSON.parse(this.responseText)]
    })
    // clear the input field
    self.setState({input: ''});
  }
}

Sorting Arrays

  • Arrays can be sorted using the .sort() method, documented here and here
  • The .sort() method can be passed a function which is used to compare each item in the array, and returns whether or not a certain item should be sorted before another item.
    • Sorting ascending by a number property
    todos.sort(function (a, b) {
      return parseFloat(a.created) - parseFloat(b.created);
    });
    • Sorting descending by a number property
    todos.sort(function (a, b) {
      return parseFloat(b.created) - parseFloat(a.created);
    });
    • Sorting alphabetically using localCompare (documented here and here)
    todos.sort(function (a, b) {
      return a.text.localeCompare(b.text);
    })

Remove Element From Array

  • When you delete a Todo, you'll need to remove it from the this.state.todos array after the DELETE Ajax has completed successfully.
  • You can remove elements from an array using .filter(). The documentation is here.
// You need the id of the todo you want to delete as a variable.
const remainingTodos = self.state.todos.filter((todo) => {
  // Looping through all todos, if the id of the current todo DOES NOT equal the id of the todo we want to delete, keep it
  if (todo.id !== id) {
    return todo;
  }
});
// Update state with filtered list using this.setState();

Creating a component for each element in an array

  • You will need to create a <Todo> component for each element in your todos array. You can do that with the .map() method documented here and here.
{this.state.todos.map((todo) =>
  <Todo key={todo.id} id={todo.id} completed={todo.completed}
    text={todo.text} removeDeletedTodo={this.removeDeletedTodo}/>
)}

componentDidMount()

  • You will need to perform your initial GET Ajax request to load existing Todos once the main App container has loaded.
  • Do this inside a componentDidMount() method on the App container.
// App.js
componentDidMount() {
  const self = this;
  // AJAX goes here
    // If AJAX successful, pare the JSON and save to state
    self.setState({todos: todos});
}

Adding Class To Todo

render() {
  var className = "todo";
  if (this.state.completed) {
    className = "todo completed";
  }
  return(
    <div className={className}> TODO GOES HERE </div> 
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment