# Rails Tutorial: Create pages

Step 1: Create rails-todolist/src/routes/AllTodos.js

We will now define a page that displays all the Todo items stored in our todos table.

Create a file rails-todolist/src/routes/AllTodos.js and paste the following code:


import React from 'react';

import { Link } from 'react-router';

import Todo from '../components/Todo';
import TodoForm from '../components/TodoForm';

import client from '@sketchpixy/rubix/lib/utils/HttpClient';

import {
  Row,
  Col,
  Grid,
  Panel,
  PanelBody,
  PanelContainer,
} from '@sketchpixy/rubix';

export default class Home extends React.Component {
  static fetchData() {
    return client.get('/todos');
  }

  constructor(props) {
    super(props);

    this.state = {
      todos: props.data.todos,
    };
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      todos: nextProps.data.todos,
    });
  }

  addTodo(todo) {
    let todos = this.state.todos.concat();
    todos.push(todo);
    this.setState({
      todos: todos,
    });
  }

  render() {
    let { todos } = this.state;

    let todosExist = todos && typeof todos.map === 'function';

    return (
      <PanelContainer>
        <Panel>
          <PanelBody style={{padding: 0, paddingBottom: 25}}>
            <Grid>
              <Row>
                <Col xs={12}>
                  <h3>Todo List:</h3>

                  <TodoForm parent={this} />

                  {todosExist && todos.map((todo) => {
                    return <Todo key={todo.id} todo={todo} />;
                  })}
                </Col>
              </Row>
            </Grid>
          </PanelBody>
        </Panel>
      </PanelContainer>
    );
  }
}

We first import Todo and TodoForm components that we defined in the previous section. We also import HttpClient and will use the client for re-fetching data on navigation.

We have also defined an AllTodos component class in the above code. This class defines a static fetchData() class method that is called before the AllTodos component is constructed. This gives us an opportunity to pre-load the component with data from the server and make it available as a prop. This is possible because we return a "promise". The static fetchData() class method returns a client.get promise. A call is then made to the URI endpoint /todos. The data fetched from the API is then passed down to the component as a prop. However, you should note that static fetchData() is not called on page load but only when page navigates using the History API. The reason for this is that when the page loads it already has the data sent from the server. Therefore, there is no reason to re-fetch the data again.

The prop data is made available to the constructor of the AllTodos component. This is where we set the initial state of the component. We also define a addTodo(todo) method which is called from the child TodoForm component whenever a new todo is created.

We then finally render the Todo form as well as the fetched todo items in the render() method.

Step 2: Create rails-todolist/src/routes/EditTodo.js

We will now define a page where we can edit a specific Todo item.

Create a file rails-todolist/src/routes/EditTodo.js and paste the following code:


import React from 'react';
import ReactDOM from 'react-dom';

import { withRouter } from 'react-router';

import {
  Row,
  Col,
  Grid,
  Form,
  Panel,
  Button,
  Checkbox,
  PanelBody,
  FormGroup,
  FormControl,
  ControlLabel,
  PanelContainer } from '@sketchpixy/rubix';

import client from '@sketchpixy/rubix/lib/utils/HttpClient';

@withRouter
class EditTodoForm extends React.Component {
  editTodo(e) {
    e.preventDefault();

    let input = ReactDOM.findDOMNode(this.input);
    let todo = input.value;
    let completed = this.checkbox.checked;

    client.put(`/todos/${this.props.todo.id}`, {
      todo,
      completed,
    }).then((result) => {
      this.props.router.push('/');
    });
  }

  componentWillReceiveProps(newProps) {
    let input = ReactDOM.findDOMNode(this.input);
    input.value = newProps.todo.todo;
    this.checkbox.checked = newProps.todo.completed;
  }

  render() {
    let { todo, completed } = this.props.todo;

    return (
      <div>
        <Form onSubmit={::this.editTodo}>
          <FormGroup controlId='todoText'>
            <ControlLabel>Todo Text</ControlLabel>
            <FormControl type='text' placeholder='A todo item...' defaultValue={todo} ref={(input) => this.input = input} autoFocus />
          </FormGroup>
          <FormGroup controlId='todoComplete'>
            <Checkbox inputRef={(checkbox) => { this.checkbox = checkbox; }} defaultChecked={completed} >
              Mark as Completed
            </Checkbox>
          </FormGroup>
          <FormGroup>
            <Button type='submit' bsStyle='blue' onlyOnHover>Update Todo</Button>
          </FormGroup>
        </Form>
      </div>
    );
  }
}

export default class EditTodo extends React.Component {
  static fetchData(props) {
    let { params } = props.routerProps;
    let { id } = params;

    return client.get(`/todos/${id}`);
  }

  render() {
    let { todo } = this.props.data;

    let editForm = todo ? (
      <EditTodoForm todo={todo} />
    ) : null;

    return (
      <PanelContainer>
        <Panel>
          <PanelBody style={{padding: 0, paddingBottom: 25}}>
            <Grid>
              <Row>
                <Col xs={12}>
                  <h3>Editing Todo Item:</h3>

                  {editForm}
                </Col>
              </Row>
            </Grid>
          </PanelBody>
        </Panel>
      </PanelContainer>
    );
  }
}

The above code renders a form wherein you can edit your existing todo item's text and change it's completion state. The form is rendered in the EditTodoForm component which when submitted calls the editTodo method. This method initiates a HTTP PUT request on /todos/${this.props.todo.id} with the changed todo and completed as parameters. If the update is successful, the page is redirected back to the homepage.

Now that we have created our pages, let us define client-side routes to these pages.