Before I directly answer your question, let me provide some background.
A gRPC channel is not actually the same as a connection. In fact, a channel may have more than one underlying connection active in parallel, depending on what load balancing policy the channel is using. The intent of the channel API is that it abstracts away the details of individual connections from the application; the idea is that the application should think of a channel as a generic interface on which RPCs are sent, and the channel will internally handle the details of connection management automatically, so the application doesn't have to worry about them.
So at a high level, I think that your application should not need to know or care whether the channel is connected. If you think it does need to know that, then I would actually consider that an indication that you need to step back and rethink how you are architecting the application.
In particular, I'm not sure exactly what you mean by "I don't want to make any of the RPC calls that we defined, as they all have effects that will occur", but that sounds a little strange to me. I assume that you mean that there could be effects on the server's state, which implies that the RPC is not idempotent. You may want to consider changing it to be idempotent somehow, since that is safer (I'll come back to why in a minute). But even if you are dealing with a non-idempotent RPC, I don't see how knowing the state of the underlying connection would help you avoid that problem. Even if the channel was connected at the moment the client initiates the RPC, there is never a guarantee that the server will see it, because the connection could fail right after the application tells gRPC to send it but before it goes out on the wire. Or the network itself could fail to deliver it to the server after gRPC puts it on the wire. Or, worse, the server could see it but the network could fail before the server's response gets back to the client. There's really no guarantee that an RPC will be atomic, nor is there any guarantee that the client and server will have the same view of whether or not the RPC succeeded. All the client knows in any of these cases is that the RPC failed, but it cannot infer from that whether or not the server actually saw the RPC.
This is why designing your RPCs to be idempotent generally makes more sense. Instead of focusing on whether or not the channel is connecteed, you can focus on whether or not individual RPCs succeed. Any time the client sees that an RPC has failed, it can retry it. And if the server happens to see the same RPC twice, that won't break things (e.g., if you replay a delete operation, it will notice that the data has already been deleted, so it will be a no-op).
So, my high-level answer is, your client code should react to individual RPC failures, not to connection failures.
All of that having been said, if you do actually want to monitor the channel's connectivity state, you can do so using the connectivity state API. Note that a gRPC channel does not actually initiate a connection immediately when you create it. Initially, it is in state IDLE. It will attempt to connect (and transition to state CONNECTING) when either (a) you send the first RPC on the channel or (b) you call channel->GetState(true) to explicitly ask it to connect. If the connection attempt fails, the channel will transition to state TRANSIENT_FAILURE, and it will retry periodically. When it has established a connection, it will transition to state READY.
Any RPC sent on a channel in state TRANSIENT_FAILURE will fail immediately, unless it is wait_for_ready. The same is true for an RPC that is still pending when the channel transitions into state TRANSIENT_FAILURE (e.g., if the channel was initially in state IDLE and started to connect as a result of the RPC being sent, but then the initial connection attempt failed).
With regard to lame channels, note that that only happens in the rare case where you try to create a channel using a malformed target URI that gRPC does not know how to parse. If gRPC knows that it will never be able to resolve the name you specified, it will return a lame channel, which is basically a channel that is permanently in state TRANSIENT_FAILURE.
I hope this information is helpful.