In this file you will find some code snippets and examples of how to do certain things in React.
In the constructor for your component, you perform several important actions.
- Calling
super()
orsuper(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 insideconstructor(props)
in this case.
- Call
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 anonSubmit
on your form, add the binding in the constructor like this:
this.addTodo = this.addTodo.bind(this);
- Anywhere other than the
constructor()
method, you modify state usingthis.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]
)};
- 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);
- Attach your event method to a component or HTML element using
onClick
,onSubmit
, etc. Documentation: https://reactjs.org/docs/handling-events.html
<button onClick={this.addTodo}>Add Todo</button>
// OR
<form onSubmit={this.addTodo}>Add Todo ... </form>
- In our Todo application, the list of todos is saved in
this.state.todos
in our container component which lives inApp.js
. Our form for creating a new Todo exists in our NewTodo component inside ofNewTodo.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 inthis.state.todos
inside ofApp.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 asonSubmit
. - 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 ouronSubmit
usingthis.props.addTodo
.
- 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 yourNewTodo
component as a prop.
- Since this state is saved in the
- 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 yourApp
component, and pass it to yourNewTodo
component as a prop. - React Form documentation here
- For a better understanding of this, see the section above on events as props, but for the assignment you will want to have your
// 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 = '';
}
- Some javascript functions will override the
this
variable with their own version of it. This is an issue in React becausethis
is how we accessthis.state
andthis.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
, savethis
to a new variable, typically calledself
. You can then access the props, state, etc likeself.state
orself.props
. - A common place to run into this is when using XMLHttpRequest for AJAX calls, the
onreadystatechange()
method overritesthis
.
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: ''});
}
}
- 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); });
todos.sort(function (a, b) { return a.text.localeCompare(b.text); })
- 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();
- 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}/>
)}
- 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});
}
render() {
var className = "todo";
if (this.state.completed) {
className = "todo completed";
}
return(
<div className={className}> TODO GOES HERE </div>
);
}