Creating Data

In the next step we will add a new route to handle creating new modules. First update the routes/modules/index.jsx to include a link to our new route by replacing

<li><a role="menuitem" tabIndex="-1">Create Module</a></li>

with

<li><Link to="new" role="menuitem" tabIndex="-1">Create Module</Link></li>

Next, add the new route into the routes/modules.jsx:

...

import New from './modules/new.jsx';

...

<Routes>
  <Route index element={Index}/>
  <Route path="new" element={New}/>
</Routes>

Note, that we do not need the pass the URL completely, since we get the modules part from the parent element. Then create this files routes/modules/new.jsx and add the following code:

import React, { useState } from 'react';
import { connect } from 'react-redux';
import { createResource } from 'redux-json-api';
import { Link, Navigate } from "react-router-dom";

function New(props) {
    const [code, setCode] = useState("");
    const [name, setName] = useState("");
    const [semester, setSemester] = useState("WS18/19");
    const [created, setCreated] = useState(false);

    function updateCode(ev) {
        setCode(ev.target.value);
    }

    function updateName(ev) {
        setName(ev.target.value);
    }

    function updateSemester(ev) {
        setSemester(ev.target.value);
    }

    function createModule(ev) {
        ev.preventDefault();
    }

    /**
    * Render the component
    */
    if (created) {
        return <Navigate to="/modules" />;
    } else {
        return (
            <main className="row justify-content-center">
                <div className="col-lg-6">
                <h1>Create new Module</h1>
                <form onSubmit={createModule}>
                    <div className="form-group">
                    <label>
                        Code
                        <input
                        className="form-control"
                        type="text"
                        onChange={updateCode}
                        />
                    </label>
                    <label>
                        Name
                        <input
                        className="form-control"
                        type="text"
                        onChange={updateName}
                        />
                    </label>
                    <label>
                        Semester
                        <select onChange={updateSemester}>
                        <option value="WS18/19">Wintersemester 18/19</option>
                        <option value="SS18">Summersemester 18</option>
                        </select>
                    </label>
                    <div className="btn-container">
                        <Link to="/modules" className="btn btn-secondary">
                        Don't create
                        </Link>
                        <button type="submit" role="button">
                        Create
                        </button>
                    </div>
                    </div>
                </form>
                </div>
            </main>
        );
    }
}

export default connect()(New);

You are already familiar with the concepts here. We define the initial state of the component, then we use a number of event listeners to update the state when the user provides input. Then, at the bottom, we use the connect to inject the store into our component. Because we are not actually going to be loading any data, we don’t need to have a mapStateToProps function.

To actually create the new module, update the createModule event handler, adding the following code:

props.dispatch(
    createResource({
    type: "modules",
    attributes: {
        code: code,
        name: name,
        semester: semester,
    },
    relationships: {
        teacher: {
        data: {
            type: "users",
            id: 1,
        },
        },
    },
    })
)
.then((data) => {
    setCreated(true);
});

As was the case in the routes/modules/index.jsx, when we loaded data, to create new data we also need to dispatch an action on the store. In this case we dispatch a createResource action, which takes as a parameter a valid JSONAPI resource. As was the case in Ember, this returns a promise. However, unlike in Ember, there is no router that we can use to redirect in the event handler. Instead the pattern in React is to set a state variable, in this case created to true. Then, if you look at the render function:

render() {
    if(created) {
        return <Navigate to="/modules" />
    } else {
        ...

you can see that we check the state of the created variable and then, if it is true, we return a <Navigate> component to initiate the actual redirect, while otherwise we return the actual code the user should see for our component. This pattern can take a bit of getting used to, but it is due to React’s almost pure component structure, which lacks the services that Ember uses to implement this kind of functionality.

Now test that you can successfully create new modules.