Haha, I did what you did, and responded before reading the entire response. Keep in mind that you are writing from the perspective of a teacher who is teaching people who have no bad habits, whereas I am writing from the perspective of someone who is often hired to clean up really big messes created by developers with a lot of bad habits. And I think that hooks make it easier to develop bad habits than other paradigms.
I find it interesting that your Class-based component does something that I edited out of an early draft:
"And yes, absolutely, this kind of junk does happen in production Class-based components. But because most people learned Classes when React was just a library and focused on the View, we all knew that people writing fetches in componentDidMount were doing the wrong thing and it was obvious technical debt that was incurred deliberately."
That applies equally to if you're hiding that implementation behind something that's injected or not--this comes from mixing the responsibility for fetching into the View layer. When you go to an implementation of withFetchTodos that does all the fetching and then injects just the todos, as in your "I just realized" implementation, _that_ is a SOLID implementation, and it could inject into a functional component, a Class component, etc., and neither the HOC nor the wrapped component needs to care.
Your Functional component examples fully commit to handling the fetch inside the component, _and they do it wrong_. In a real project, you're going to have people seeing that a senior dev wrote this code and are going to copy and paste it everywhere. And good luck tracking that down to fix it when someone realizes well oops, then is sometimes called after the component unmounts and setTodos is out of scope. This can also occur in a poorly-written Class component, as you noted, but that's _why_ you incur technical debt by mixing responsibilities. Which hooks encourage you to do by dragging absolutely all your dependencies into the component. Class-based components _allow_ that, but they're unopinionated. You _can_ create a mess, but you're not pushed into it.