If you want to disable a button when an input string is empty, then the only state you need is the value of the input string.
const [inputVal, setInputVal] = useState('') // ... <input value={inputVal} onChange={e => setInputVal(e.target.value)} /> // ... <button disabled={!inputVal}> /* ... */ </button> Here we connect the input component to the state value. This is called a controlled component, because its value is controlled from by an external source (the state value) as opposed to an uncontrolled component, which means the input element holds it's own internal state (the default way inputs work if you don't set their value prop directly.
When the input component receives input (such as someone typing a character) the onChange prop is called. All we do then is take the new value of the input element (e.target.value) and use it to set the state.
If you can derive state from other state, then you shouldn't be storing it in state. Having a state variable called disabled only makes things more complex. The general idea is to use as little state as possible, and compute as much as you can from that state.