What you specify after bind r should be one or more tmux commands (with or without arguments). source-file ~/.tmux.conf is a valid tmux command. source-file ~/.tmux.conf && echo reloaded. is not. To run more than one tmux command you need to use ; (sometimes in a form of \;, see Binding a tmux key to multiple commands for details). But echo is not a tmux command.
There is however the run-shell tmux command that allows you to run arbitrary code in a shell. My tests tell me the shell is sh (even if my command interpreter is not sh). The manual confirms:
shell-command arguments are sh(1) commands.
To run source-file from a shell you need to call tmux source-file …. This line in .tmux.conf should work:
bind-key r run-shell 'tmux source-file ~/.tmux.conf && echo reloaded'
The output from the shell (i.e. from echo in this case) will appear in view mode. Personally I would like the short message to appear in the status line, so I would rather use this variant:
bind-key r run-shell 'tmux source-file ~/.tmux.conf && tmux display-message reloaded'
It's quite educative to analyze what happens when you trigger this binding. You're in a tmux client that communicates with a tmux server. When the client receives a proper key sequence, prefixr, it informs the server and the server acts according to the binding: it runs a shell. The shell runs another tmux client that communicates with the same tmux server and makes it execute the source-file … command. Then the shell runs yet another tmux client that communicates with the server and tells it to run display-message ….
In other words the server runs some code that spawns clients that tell the same server to do something.
Your original working binding (bind r source-file ~/.tmux.conf) is a lot simpler, the server runs source-file … directly.
Since source-file and display-message are tmux commands, you can run them both upon a keystroke without spawning a shell:
bind-key r source-file ~/.tmux.conf \; display-message reloaded
One would expect \; here to work like ; in a shell, but in fact it's more like &&. The manual states:
Each command is terminated by a newline or a semicolon (;). Commands separated by semicolons together form a command sequence - if a command in the sequence encounters an error, no subsequent commands are executed.
So this last binding seems just right in your case, it doesn't spawn any unnecessary shell. Still it's good to know you can run anything via run-shell, even additional tmux clients.
You can mix the two approaches:
bind-key r source-file ~/.tmux.conf \; run-shell '#some complex logic here or whatever'
And there is if-shell. Example:
bind-key r if-shell 'tmux source-file ~/.tmux.conf' 'display-message reloaded' 'display-message "reload failed"'