In my previous post I was able to render letters that had randomly assigned font values by using this code in the render function of the component:

render: function() {
  var fonts = this.state.fontStyles
  var letters = this.state.value.split("").map(function(letter){
    var i = Math.floor((Math.random() * fonts.length))
    return (
      <span style={ { fontFamily: fonts[i] } }>{letter}</span>
    )
  })
  return (
    <div>
      <div className="input" style={ { float: "left" } }>
        <textarea rows='20' cols='25' type="text" onInput={this.showOff}></textarea>
      </div>
      <div className="output"
        style={ { float: "right", letterSpacing: '1em', fontSize: '1.25em'} } >
          <p>{letters}</p>
      </div>
    </div>
  )
}

While it did what I wanted it to by rendering letters in random font styles, it did so for EVERY letter, not just the most recently inputted character. Not quite what I had in mind, but the fortunately the solution wasn’t too far off.

If I want to isolate the reactive elements of render (the random function, primarily). I ought to do this inside showOff whenever the event onInput gets triggered for two reasons:

  1. The random function inside render makes preserving the state pointless.
  2. If I want to preserve the history, I ought to use a better container for it - an array of objects - and set that as a mutable state property.
getInitialState: function() {
  // an array is used instead of an empty string as a default state property
  return { letters: [] }
},

showOff: function(e) {

  // pull the input text and the `letters` property
  var text = e.target.value.split("");
  var letters = this.state.letters;
  var i = Math.floor((Math.random() * fonts.length))

  // look at the last letter
  var lastLetter = text[text.length - 1];

  // check to see if a character was deleted
  if (text.length < letters.length) {
    // pop it out if so
    letters.pop();
  } else {
    // otherwise, put in a new one, preserving both the font and the letter
    letters.push({character: lastLetter, font: fonts[i]})
  }

  // state is changed, and render then triggers automatically.
  this.setState({
    letters: letters
  })
}

Change the map function in render to take into account each element being an object instead of a string, et voilà, it works…mostly. Remove any size of a chunk from the middle, and it acts weird. What now?