Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Use class reference for state in useState hook

I’d like to store a reference to a class in state but an error occurs.

Specifically, the following:


useState(SomeClass)

results in:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

 Class constructor SomeClass cannot be invoked without 'new'

🤔…

I can find another route for my specific need but I’d like to understand what the issue is here exactly.

FWIW here’s a concrete situation where the above would be useful:

Consider a component that displays a list of Super[] but filtered by subclass. The component contains a set of radio buttons to set the filtering. e.g.

const FilterableListView = (props:{items:SuperType[]})=>{

const [SelectedType,setType] = useState(DefaultType as SuperType)// where DefaultType extends SuperType

const itemsToDisplay = props.items.filter(item=>item instanceof SelectedType) 

// in radio button callback somewhere...
setType(SomeOtherSubType)

...

}

Here the state is the filtering criteria. No need for instances of any of the class references.

Again, I can skin this cat in many ways — I just would like to know why useState(Class) results an attempt to instantiate Class.

>Solution :

The reason you cannot use a class as state is because useState allows for Lazy initial state.

The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:

// lazy initial state example
const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

One possible solution is to wrap your class in an object, like { maker: MyClass }. Here is a minimal complete example. Click the create to create objects. Click ⚫️ or ⬛️ to change the type of object that is created.

class Circle {
  constructor() { this.value = "⚫️" }
}

class Square {
  constructor() { this.value = "⬛️" }
}

function App() {
  const [mode, setMode] = React.useState({ maker: Circle })
  const [elements, setElements] = React.useState([])
  return <div>
    <button
      onClick={_ => setMode({ maker: Square })}
      disabled={mode.maker === Square}
      children="⬛️"
    />
    <button
      onClick={_ => setMode({ maker: Circle })}
      disabled={mode.maker === Circle}
      children="⚫️"
    />
    <button
      onClick={_ => setElements([...elements, new mode.maker()])}
      children="create"
    />
    {elements.map(e => <div className="element">{e.value}</div>)}
  </div>
}

ReactDOM.render(<App/>, document.querySelector("#app"))
.element {
  display: inline-block;
  margin: 5px;
}
button[disabled] {
  border-color: red;
  border-radius: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading