The conventional way to do this is to use a circular buffer (sometimes also referred to as a circular queue). The Rx interrupt routine inserts received data bytes into the queue using its input pointer/index. The mainline code uses the output pointer/index to extract the data from the queue.
The big advantage with this procedure is to allow the Rx data to arrive in spurts that may may come faster than they can be consumed by the mainline loop due to latency in other tasks. As long as the queue depth is enough and the mainline code is able to consume the Rx data at least as fast as the long term data flow rate this is the way to go.
Note that there is a plethora of information online regarding the design, coding and operation of circular queues. One hint is that there can be a performance improvement in the use of circular queues if the depth of the queue is kept to an even power of two number of bytes. When this is done the queue index wrap from top to bottom of the queue can become a simple mask of the index with a value of (queue depth - 1).