Game of Thrones Quiz Game with React and GraphQL: Create the Questions Form
Publikováno: 17.6.2019
We’re going to make use of some new packages for creating questions. Open a terminal in your project folder and run the following command:
npm install react-mod...
We’re going to make use of some new packages for creating questions. Open a terminal in your project folder and run the following command:
npm install react-modal react-tagsinput react-toastify 8base/file-input
Let’s go through the purpose of each package:
react-modal
- this package is useful for creating modals, and our question form will be displayed on a modal so this comes in handy.react-tagsinput
- each question requiresoptions
and with this package we can display an input field where multiple string values can be entered.react-toastify
- a great toast library for displaying a message after the question is created successfully.8base/file-input
- a library from 8base for managing file uploads in GraphQL.
Question form
Let’s create the question form, this will be a simple form wrapped with a GraphQL mutation. Let’s go ahead and create a folder called question-form
in the components
directory. Within the question-form
folder, create a file index.js
.
The first thing we’ll do is define the mutation for the form. Open the file and copy the snippet into the file:
import gql from "graphql-tag";
const QUESTION_CREATE_MUTATION = gql`mutation QuestionCreate($data: QuestionCreateInput!) {
questionCreate(data: $data) {
id
}
}`;
Next, we’ll create the QuestionForm
component, the component will be wrapped by the graphql
higher order component. Update the file with the snippet below:
import React, { useState } from 'react';
import TagsInput from 'react-tagsinput';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { FileInput } from '@8base/file-input';
import { toast } from 'react-toastify';
import 'react-tagsinput/react-tagsinput.css';
import './form.css';
const QuestionForm = ({ closeModal, questionCreate }) => {
const [questionForm, setQuestion] = useState({
options: [],
question: '',
image: {},
answer: '',
});
const inputProps = {
placeholder: 'Add an option and press enter',
className: 'question-input',
};
const handleSubmit = async (e) => {
// TODO -- 2
};
const handleInputChange = (event) => {
// TODO -- 3
};
const handleTagsChange = (options) => {
// TODO -- 4
};
const handleImageChange = (value) => {
// TODO -- 5
};
return (
// TODO -- 1
);
};
const QUESTION_CREATE_MUTATION = gql`mutation QuestionCreate($data: QuestionCreateInput!) {
questionCreate(data: $data) {
id
}
}`;
export default graphql(QUESTION_CREATE_MUTATION, {
name: 'questionCreate',
})(QuestionForm);
Let’s go through what’s going on in the snippet:
- The
QuestionForm
component, that takes two propscloseModal
andquestionCreate
. ThecloseModal
function is passed from the modal component, it closes the modal when called. While thequestionCreate
prop is passed from thegraphql
HOC for mutation, the function will be passed the data required for creating a question. - The
inputProps
object will be passed to an input element for defining it’splaceholder
andclassName
properties. - And finally and a whole lot of TODOs. We’ll run through filling each gap as we progress.
First, let’s add the return value of the component, replace the TODO
--
1
comment with the snippet below:
<form action="" id="question-form" onSubmit={handleSubmit}>
<input
type="text"
name="question"
placeholder="Enter your GOT related question...."
onChange={handleInputChange}
value={questionForm.question}
className="question-input"
/>
<TagsInput
value={questionForm.options}
onChange={handleTagsChange}
maxTags={4}
inputProps={inputProps}
/>
<input
type="text"
placeholder="Add the answer to the question..."
value={questionForm.answer}
onChange={handleInputChange}
className="question-input"
name="answer"
/>
<FileInput
onChange={handleImageChange}
value={questionForm.image}
maxFiles={1}
name="image"
>
{({ pick, value }) => (
<div className="image-area">
<button type="button" onClick={pick} className="image-upload">
Choose File
</button>
<p style={{ whiteSpace: 'nowrap' }}>
{value
? Array.isArray(value)
? `${value.length} files selected`
: value.filename
: 'No files selected'}
</p>
</div>
)}
</FileInput>
<div className="submit-area">
<button className="submit-button" type="submit">
Create Question
</button>
</div>
</form>
The FileInput
library manages file upload, it uses FileStack to handle uploads returning the fileId
and fileName
. 8base uses this to identify your uploads.
Next, let’s handle form submission, replace the TODO
--
2
comment with the snippet below:
const handleSubmit = async (e) => {
e.preventDefault();
await questionCreate({ variables: { data: questionForm } });
closeModal();
toast('Your question has been created successfully');
};
Here’s what is going on:
- We pass an object as an argument to the
questionCreate
function. The object contains the data for creating a question. - When the request for creating a question is complete, we close the modal by calling the
closeModal
function and displaying atoast
.
To handle input change events, replace the TODO
--
3
comment with the snippet below:
const handleInputChange = (event) => {
event.preventDefault();
const {
target: { name, value },
} = event;
setQuestion({
...questionForm,
[name]: value,
});
};
In the snippet above, we get the name
and value
from the event target, using these values we update the questionForm
state value.
Finally, let’s replace comments TODO
--
4
and TODO
--
5
with the snippets below:
// TODO -- 4
const handleTagsChange = (options) => {
setQuestion({
...questionForm,
options,
});
};
The tags input returns an array of strings entered by the user. The returned values are added to state. To enter a new value after typing, you need to press enter before the change event is triggered. Next, replace TODO
--
5
with the snippet below:
const handleImageChange = (value) => {
setQuestion({
...questionForm,
image: { create: value },
});
};
After uploading the image, the File-Input
component returns an object containing the following fields:
{
fileId: '',
filename: '',
public: true
}
The fileId
represents the unique identifier for the uploaded image, the filename
and public
fields are also required for file upload on 8base.
The stylesheet for this component can be found here. Create a CSS file named form.css
in the src/components/question-form
directory and copy the contents of the file on GitHub into it.
Next, let’s create the question modal component and render the QuestionForm
component within it.