An extended project

Over the next few lectures I am going to be developing an extended example of a system implemented in React. The example system that I am going to develop is a system that will allow users to construct and distribute surveys and then view the collected responses from those surveys. The system will consist of three separate applications:

Our first application: constructing surveys

In these notes I am going to begin the process of building the first of these three survey applications. As I work my way through building this application I will be also taking this as an opportunity to introduce some important new ideas in React programming.

Installing some additional components

One of the nice things about React is that programmers can take advantage of a lot of pre-built components. Our first application uses a couple of components from the react-components package. To use these components we need to make sure that they are installed in our project. To do this, we open a terminal on the project folder and run the command

npm install @sajari/react-components

Version zero of the survey build app

The surveys that we are going to create will consist of a list of questions. We want to be able to ask two types of questions: questions with a fixed set of possible responses, and free-response questions where users can enter any answer they want. Each question we display will consist of a prompt and either a set of radio buttons for multiple-choice questions or a text input field for free-response questions. Given this, the first React component I am going to design will be a Question component that displays a single question.

To use this Question component to ask questions we can create Question elements by doing something like this:

<Question prompt="What is your favorite color?" choices="red,green,blue" />
<Question prompt="What is your favorite song?" choices="" />

To ask a multiple choice question we will provide a comma separated list of options in the Question's choices attribute. For a free-response question we will leave the choices attribute blank.

This example demonstrates a very common feature of React components: components can have attributes associated with them that control the appearance and behavior of that component.

Attribute values get passed to React components as props, usually in the form of a props object that gets passed as a parameter to the component function. Inside the props object there will be a property for each of the attributes passed in to the component, and the value of the property will that attribute's value.

Here now is the first version of our Question component:

function Question(props) {
  if(props.choices=='') {
    return (
      <div>
        <p>{props.prompt}</p>
        <input type="text"/>
      </div>
    )
  } else {
    const options = props.choices.split(',');
    return (
      <div>
        <p>{props.prompt}</p>
        <RadioGroup inline defaultValue={options[0]}>
        {options.map((o) => {return(<Radio value={o}>{o}</Radio>)})}
        </RadioGroup>
      </div>
    )
  }
}

An alternative to using a props parameter is to use a destructuring assignment for the parameter. This syntax breaks the props object into a list of separate properties where each property has its own separate parameter:

function Question({prompt,choices}) {
  if(choices=='') {
    return (
      <div>
        <p>{prompt}</p>
        <input type="text"/>
      </div>
    )
  } else {
    const options = choices.split(',');
    return (
      <div>
        <p>{prompt}</p>
        <RadioGroup inline defaultValue={options[0]}>
        {options.map((o) => {return(<Radio value={o}>{o}</Radio>)})}
        </RadioGroup>
      </div>
    )
  }
}

As you can see here, this component renders itself in one of two different ways depending on the value of the choices parameter. If the value of choices is a comma separated string we will want to display the options as a list of radio buttons. To implement the radio buttons I am using a combination of the react-components Radio and RadioGroup components.

To use these components in my App.jsx file I need to add an import statement:

import { Radio, RadioGroup } from '@sajari/react-components';

Using map() to create multiple components

For multiple choice questions we will need to make a Radio component for each of the available choices. For example, we could do something like this:

<p>What is your favorite color?</p>
<RadioGroup inline defaultValue="red">
  <Radio value="red">Red</Radio>
  <Radio value="green">Green</Radio>
  <Radio value="blue">Blue</Radio>
</RadioGroup>

To automate making the Radio elements we will need something like a loop that iterates over an array of choices. Instead of writing a loop, we will instead the very powerful map() method that JavaScript provides for arrays. The map() method takes as its parameter a function that will get applied to each element of the array. This approach leads to the code I actually used to make the Radio elements:

const options = choices.split(',');
return (
  <div>
    <p>{prompt}</p>
    <RadioGroup inline defaultValue={options[0]}>
      {options.map((o) => {return(<Radio value={o}>{o}</Radio>)})}
    </RadioGroup>
  </div>
 )

Now that we have a Question component that can display the questions, we can go ahead and write a simple test application to test our component.

In the code for the App component I am going to use the same mapping trick to generate a sequence of Question elements.

function App() {
  const examples = [{prompt:"What is your favorite color?",choices:"Red,Green,Blue"},
                    {prompt:"What is your favorite song?",choices:""}];

  return (
    <div>
      {examples.map((e)=>{return (<Question prompt={e.prompt} choices={e.choices} />)})}
    </div>
  )
}