You are conflating tooling and platform.
Truffle is tooling. You don't deploy to truffle, but it can be handy to use truffle to deploy to dev/test and production platforms.
Ganache is platform. Regardless of tooling (in your case, a React app), Ganache is a valid platform for dev/test both in terms of cost and in terms of developer productivity. Consider using that as you iterate over your React app.
You should probably consider platform dependencies for your React app. Will you proceed on the assumption that users have a full node nicely synced up, do you want to minimize dependencies or do you want to offer a choice? You'll need a Web3 "provider". If you so choose, you can have multiple providers conditionally chosen at runtime, i.e. Look for full node or fall back to Infura if there is no full node.
Deploying a contract involves a specially crafted transaction, signed by the deployer and sent to address 0x0
. In the simple case, it's the same contract every time, so you can compile it outside your app and treat the bytecode to deploy as data known to the app. A JavaScript app can compile source code on-the-fly if you need that instead.
Web3 itself has gone through significant changes, so many of the examples found online are outdated. I did some searching for a recent example with reasonably concise code. This node
example seems appropriate and hopefully, you can extrapolate from that for your React implementation. I realize it's an issue report.
https://github.com/ethereum/web3.js/issues/2446
Hope it helps.