The problem is when the components which I want to pass the state to are themselves part of a state.
This is indeed the problem, and an anti-pattern in React which as you've discovered creates stale values. Ideally you should not be keeping any components in state, and if you do, they must be completely pure (i.e. not liable to recieve new props during their life cycle).
What's actually happening when you call this state setter:
props.setitems(items=>items.concat(<tr><td><Button {...props}/></td></tr>))
Is this, assuming it's the first time its being called:
<Button counter={0} ... />
Not only will that button only ever have the value 0
as its counter, but any buttons it creates in turn will have the same value. What you're talking about doesn't actually make sense in a React context because the whole point is to break referentially (and strict equality more generally) in order to trigger rerenders and effects.
We want to keep our state immutable which means recreating any parts of it which change on every render. For what you're doing to work, you would have to recreate the entire store of buttons by looping as many times as the length of items
whenever you create a new one.
The way you should be thinking about this is how to derive the UI from data, not store it as data:
function Button(props){
return <button onClick={()=>{
props.setcounter(cur=>cur+1);
props.setitems(items=> [...items, 1])
}}>
{props.counter}
</button>
}
function App() {
const [items,setitems]=React.useState([1]);
const [counter,setcounter]=React.useState(0);
return (
<div className="App">
{items.map(() => <Button counter={counter} setcounter={setcounter} setitems={setitems}/>)}
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…