Randomness can be completely tossed as well as any hashing if desired. I'm personally too lazy to consume from storage, so if you want to avoid hashing and random, I'd suggest a simple call to
microtime(true); would be sufficient (even just time()).
The keys to keep it simple here are:
1. Always generate a token
2. Always abandon or destroy a token when consumed
3. Always generate a token so it has a reasonably low probability of collision
So long as a resend of the post doesn't match a new token (or any token), then you can safely assume that its been consumed already.
I'll double check that this works properly whilst holding down the submit button with enter. I'll have to test that when I get home.