Creating todos will require a form on the client side. In this sprint, you'll create a CreateTodoForm
component to handle that form. The new component will join TodoList
as one of the children of the TodosContainer
component.
- Create a file called
src/components/CreateTodoForm.js
, and add the following code:
// src/components/CreateTodoForm.js
import React, {Component} from 'react'
class CreateTodoForm extends Component {
render(){
return (
<div className='createForm todoForm'>
<h2>Create Todo Here!</h2>
<form>
<input
placeholder='Write a todo here ...'
type='text'
value='write a new todo' />
<button type='submit'>Create Todo!</button>
</form>
</div>
)
}
}
export default CreateTodoForm
- Add a
CreateTodoForm
component to theTodosContainer
component'srender
method.
render(){
return (
<div className='todosContainer'>
<CreateTodoForm />
<TodoList
todos={this.state.todos} />
</div>
)
}
Don't forget to
import
your new component as well!
-
Briefly try out the form on the
/todos
page. Identify which part of your code set up the text inside the input box. What happens if you submit the form? What happens if you try to change the input? -
Instead of the
value
of the input box staying the same, it will be tied into the component's state, and it will change when users type in the input box. To get started, set upstate
in theCreateTodoForm
component.
// inside src/components/CreateTodoForm.js
class CreateTodoForm extends Component {
constructor(){
// use Component's constructor
super()
// set initial state
this.state = {
todo: ''
}
}
render(){
return (
<div className='createForm todoForm'>
<h2>Create Todo Here!</h2>
<form>
<input
placeholder='Write a todo here ...'
type='text'
value={this.state.todo} />
<button type='submit'>Create Todo!</button>
</form>
</div>
)
}
}
- Use the code below to add an
onChange
event handler to theinput
text box:
<input
onChange={event => this.onInputChange(event)}
placeholder='Write a todo here ...'
type='text'
value={this.state.todo} />
onChange
is reserved JSX to define an event for an input change, almost identical tong-change
in angular
- Think critically about the code above. What kind of value is the JSX going to add in for the
onChange
attribute?
click for answer
The value inside{}
is an anonymous function that takes in an event and calls another function with that event as the argument.- For the event handler to keep
state
updated, there must be athis.onInputChange
function. Add theonInputChange
function to theCreateTodoForm
class.
class CreateTodoForm extends Component {
constructor(){
super()
this.state = {
todo: ''
}
}
onInputChange(event){
console.log('create todo input changed')
}
// ...
- Modify the
onInputChange
function so that instead of logging a message to the console, it changes the state. Thetodo
in the state should have valueevent.target.value
.
Hint: for an example of setting state, you can reference the TodosContainer
class's fetchData
method.
- When the form is submitted, the page currently refreshes. Instead, it should make an AJAX request to the super-crud API to add the new todo. In the JSX code for the component, add an
onSubmit
attribute to theform
element:
<form onSubmit={event => this.onFormSubmit(event)}>
- Create an
onFormSubmit
method inside theCreateTodoForm
component class.
onFormSubmit(event){
console.log('form submitted')
event.preventDefault()
let newTodo = this.state.todo
this.props.createTodo(newTodo)
this.setState({
todo: ''
})
}
onSubmit
is reserved JSX to define an event for form submission, almost identical tong-submit
in angular
You should see an error in your browser console.
- Think critically about the code snippet above.
-
What does
event.PreventDefault()
do? -
What will the value of
this.state.todo
be when the form is submitted? -
Is
this.props.createTodo
already a function? (Check where theCreateTodoForm
isrender
ed in theTodosContainer
class.)
- Since
createTodo
is an attribute of aCreateTodoForm
component'sprop
, it needs to be supplied by the parent component. Update thesrc/containers/TodosContainer.js
to pass acreateTodo
function into the form component:
// src/containers/TodosContainer.js
// ...
createTodo(newBody) {
console.log('creating todo', newBody)
}
render(){
return (
<div className="todosContainer">
<CreateTodoForm
createTodo={this.createTodo.bind(this)} />
<TodoList
todos={this.state.todos} />
</div>
)
}
- Think critically about the code above.
The
render
method forTodosContainer
passes thecreateTodo
function of the container component TO theCreateTodoForm
component.
The
bind(this)
portion of the code means thecreateTodo
function will use THISTodosContainer
component asthis
, even if it's called from a different part of the code (like it will be, insideCreateTodoForm
'sonFormSubmit
method).
- The
createTodo
method should use the todo body passed in to make an AJAX request to the server. AJAX is the role of theTodoModel
class, though, so add a staticcreate
method to that model class.
static create(todo) {
let request = $.ajax({
url: "https://super-crud.herokuapp.com/todos",
method: 'POST',
data: todo
})
return request
}
- Back in the
TodoContainer
class'screateTodo
method, group the form data about a todo in an object and store it in a variable. Then, pass that object into theTodoModel.create
method.
createTodo(newBody) {
let newTodo = {
body: newBody,
completed: false
}
TodoModel.create(newTodo).then((res) => {
console.log('created todo', res)
let todos = this.state.todos
let newTodos = todos.push(res)
this.setState({newTodos})
})
}
- Think critically about the code above. What happens after the todo is successfully created?
click for an answer
A newtodos
variable contains all the current todos from the TodosContainer
state. It adds the new todo from the response into a newTodos
array, after all of the original todos. Then, it sets the component's state to the new array.Remember that in the submit event of the form, we used a function this.props.createTodo()
:
In src/components/CreateTodoForm
:
onFormSubmit(event){
event.preventDefault()
let newTodo = this.state.todo
this.props.createTodo(newTodo)
this.setState({
todo: ""
})
}
We pass createTodo
from the container as props
. In src/containers/TodosContainer.js
:
render(){
return (
<div className='todosContainer'>
<CreateTodoForm
createTodo={this.createTodo.bind(this)} />
<TodoList
todos={this.state.todos} />
</div>
)
}
The argument passed in at the CreateTodoForm
level(child) was state from that component. And now it updates state at the TodosContainer
level(parent).