React Principles: Can ControlledInput be broken down into further components?

Hi all.

I am on the Front End Libraries > Markdown Previewer project. I have the project coded and is passing all tests. It consists of a single React component. I think this project can be broken down into further components.

My project’s component currently does three things: it reads input text from a textfield, converts it to markup, and outputs it.

Is it more correct to split this down further? For example ComponentOne reads the input text and converts it, ComponentTwo outputs it. Perhaps even a third component to handle the conversion?

I’m struggling to “think in React” and my research hasn’t made it any clearer for me so far. If any of you can point me in the right direction I would appreciate it. Thanks!

If you need to do it, do it, if you don’t, don’t. This seems simplistic advice, but it’s not really possible to give you detailed advice without seeing code examples.

It is easier to understand programs if they are

  1. short,
  2. simple, and
  3. in one file.

As a rule the more files you have, the more difficult it becomes to navigate and edit. The more complex the logic in individual files becomes, the more difficult it becomes to understand and debug. The longer the individual files become, the more difficult it becomes to navigate and understand the flow of the logic.

Also, the more complex a single unit is, the more difficult it becomes to test and debug. But for things to be useful they often need complexity, it is the wrong move to over-generalise and over-abstract.

The easiest way to deal with this kind of complexity is often to split the bits of UI logic into different files/components. But this is completely context sensitive and shouldn’t be done for the sake of doing it. It’s an abstraction, and premature abstraction is dangerous. It may well reduce the complexity of individual components, but you then have a number of components that need to link to one another somehow – you are possibly just trading one type of complexity for another.

It’s fine to break any of these rules, and in practise that will almost always happen (as an example, it would make sense if you were writing a markdown parser to make it a single complex function).

You have a function that takes some text input and passes it through a markdown processor to generate some output. You’re using React to flatten the complexity of the UI down to a handful of functions that you call, and you’re using the markdown parser to flatten the complexity of parsing down to a single function.

If it lives in a single component, dealing with state becomes a lot easier because you don’t have to pass anything between components. It also colocates all of the code, so there is no navigating between files: everything that works together in the UI lives together in the same place in the code.

On the other hand, if you split out the components, they become much easier to unit test and may be easier to reason about.

1 Like

Hi Dan, thanks for your reply. I think a single component gets the job done and is easy to follow because the app is simplistic. On the other, I think separating the UI from the Input code and Output code better suits the principles of React because in this scenario I am plugging components into the UI. I added a code example below, I hope you can take a look.

In my code example below, I have two components: ControlledInput reads the input text and converts it. MarkupOutput sets the output. I’m using Material-UI for style control. In this example, ControlledInput is also controlling the UI.

Going further, I think it makes sense to separate the bulk of my Grid UI from the ControlledInput functionality… but then I would need to implement Redux to pass information between sibling components ControlledInput and MarkupOutput. I’m not sure I want to do this because I want have a solid grasp on React before overwhelming myself with Redux.

The parent-child relationship is easier now but I think I’m doing something wrong by putting most of the UI code in ControlledInput. What do you think?

class ControlledInput extends React.Component {
  constructor() {
    super();
    this.state = {
      input: initText
    };
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
      this.setState({
        input: event.target.value
      })
    }

  getMarkupText(userInput) {
    let rawMarkup = marked(userInput, {sanitize: true});
    return { __html: rawMarkup };
  }

  render() {
    return(
      <div>
      <Grid
        container>
        <Grid
          item>
          <Paper
            className={styles.paper}>
            <div>
              <h4>Raw input</h4>
              <textarea
                id="editor"
                value={this.state.input}
                onChange={this.handleChange}>
              </textarea>
          </div>
          </Paper>
        </Grid>
        <Grid
          item>
          <Paper>
            <MarkupOutput output={this.getMarkupText(this.state.input)}/>
          </Paper>
        </Grid>
      </Grid>
    </div>
    )
  }
}

class MarkupOutput extends React.Component {

  render() {
    return (
      <div>
        <div style={divText}>
          <h4>Controlled Input</h4>
          <div
            id="preview"
            dangerouslySetInnerHTML={this.props.output}>
          </div>
        </div>
      </div>
    );
  }
};

function App() {
    return (
      <div>
        <ControlledInput />
      </div>
    )
}

ReactDOM.render(
  <App />,
  document.getElementById("app")
);

So React is the UI, there isn’t a separation here. The “Grid UI” is already separated out, that’s what the material UI library is. This is what I mean by abstraction being dangerous if you’re not careful: what you’re talking about is taking something that has already been packaged up and separated and wrapping it in more and more layers of abstraction: you are not simplifying by doing that, you are actually making it more complicated.

You absolutely do not need Redux to communicate between sibling components. You do what React does just fine: move the common state to a parent component, pass it down to the children. If that becomes too onerous, react Context can be used, but it makes things more complicated so needs to be used with great care. Redux is another layer of complexity and best used when you have multiple sources of data (that you need to marshal into shape) that then need to stay in sync and be accessed by various different parts of the app: what you have here is not that.

There is good reason to separate the components, but the reason is for performance, so React can work to the best of its ability. Always remember that this is why you cut components up into other components, it’s not just to suit how you want the code to look. A component rerenders if the state or the props change, so the reason for seperating is to cut down on rerenders: React is very very fast, so often this don’t matter much, but you definitely don’t want all the UI on the page rerendering every time a single thing changes. In this case, doesn’t matter: you do want things to rerender because you’re running the conversion in realtime, so everything updates when the user types.

Example (I’ve not included the grid stuff), I’ve used functional components rather than classes but principle is exactly the same:

1 Like