React

by nbrustein

HTML

<div id="app"></div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.done {
  color: rgba(0, 0, 0, 0.3);
  text-decoration: line-through;
}

input {
  margin-right: 5px;
}

React

const { useState, useReducer } = React;

/*
	This first component does not work because, even
  after running useReducer, myModel is still the same
  model.  Nothing has changed, so react does not re-render.
*/
function DontWork() {

	const myModel = useModel()[0];
  
  const updateProperty = useReducer((myModel, newValue) => {
  	myModel.property = newValue;
    return myModel;
  }, myModel)[1];

  return (
    <div>
      <h1>Don't work</h1>
      <input
        onChange={
        	(e) => {
            updateProperty(e.target.value);
          }
        }
      /> 
      <span>{myModel.property}</span>
    </div>
  )
}

/*
	It works if I'm willing to clone the model, but that seems like a waste, especially if the model is more complex.
  
  But, I'm also not much of a functional programmer, so maybe I should just get over it and accept that cloning things is good in functional programming?
*/
function WorksIfICloneIt() {

	const [myModel, updateModel] = useModel();  
  
  const updateProperty = function(newValue) {
  	const clone = new Model({property: newValue});
    updateModel(clone);
  }

  return (
    <div>
      <h1>Works if I clone it</h1>
      <input
        onChange={
        	(e) => {
            updateProperty(e.target.value);
          }
        }
      /> 
      <span>{myModel.property}</span>
    </div>
  )
}


/*
	I can make it work using the forceUpdate strategy
  at https://medium.com/crowdbotics/how-to-use-usereducer-in-react-hooks-for-performance-optimization-ecafca9e7bf5
  
  This makes it work, but there are two things I don't like about it:
  
  1. React recommends against using forceUpdate, and I'm not comfortable enough with my React skills to know when it's okay to ignore that warning.
  2. If I were to run into this problem again and again, I would always end up having to use useReducer and useForceUpdate and then calling both updateProperty and forcceUpdate in the onChange.  Not totally dry.
*/
function WorksUgly() {

	const myModel...