1

This is an example of something I'd like to understand better syntactically in JSX.

Problem:

This works:

<button
  onClick={ !isRecording ? beginRecording : endRecording } > 
</button>

and this works:

<button
  onClick={ () => { modalPortal.current.open() } } > 
</button>

<Modal ref={modalPortal}>
    <h1>Congratulations!</h1> 
    <p>If this modal opened, you find javascript syntax seamless and intuitive</p>
</Modal>

Together, no bueno.

<button
    onClick={!isRecording ? () => {modalPortal.current.open();beginRecording} : endRecording } > 
</button>

Error:

react-expected-an-assignment-or-function-call-and-instead-saw-an-expression


Detail:

This is inside a function component. isRecording & endRecording etc are are states in an object within the scope of the function component which defines the page I'm on in the app, modalPortal is a reference:

export default function RecordPage() 
{
    let [audioURL, isRecording, beginRecording, endRecording, timer] = RecorderContext(); 
    const modalPortal = useRef(null);
...
}

I've also tried various permutations of passing this out to a single function that does the conditional evaluation etc

onClick={ doManyThings() } > 

With and without arrows, both kinds of brackets and passing in arguments and without, none of it seems to work. I'd love a clear answer from someone knowledgeable!


References to things I've tried that don't work:

Conditional onClick from ternary

Call multiple functions onClick ReactJS

Setting conditional onClick behaviour in React Component

React: Expected an assignment or function call and instead saw an expression

lys
  • 949
  • 2
  • 9
  • 33
  • 1
    Use `beginRecording()` and `endRecording()` if you're going to use the second syntax. You can't mix and match function calls and function references like that. Or, just have a function that encapsulates the ternary and stop doing conditional logic in the views. – Heretic Monkey Mar 09 '21 at 00:51
  • You just need to do `onClick={ () => doManyThings() }` – Yadab Mar 09 '21 at 00:56
  • @Yadab - tried that - no, it was indeed the missing brackets. I'm yet to understand why it's syntactically valid to drop them in one context and cryptically halt the compiler in another – lys Mar 09 '21 at 01:13
  • 1
    A function name is just a reference to that function, i.e. `doManyThings`, and when you add the parenthesis, `doManyThings()`, you are invoking that function. `onClick={doManyThings}` is very nearly a functional equivalent to `onClick={() => doManyThings()}`. In many cases in React you'll see the parens dropped for a couple reasons: (1) the onClick callback doesn't care about the click event, and (2) the callback doesn't need to be passed any arguments. – Drew Reese Mar 09 '21 at 01:18

2 Answers2

2

You can move the ternary/branching logic into a single callback function.

<button
  onClick={() => {
    if (!isRecording) {
      modalPortal.current.open();
      beginRecording();
    } else {
      endRecording();
    }
  }
>
  ...
</button>

If you want to continue using the ternary though you need to also invoke the beginRecording function. The idea is similar here, based on the condition, return an anonymous function that does a couple things, or return a function reference that does whatever it does.

<button
  onClick={!isRecording 
    ? () => {
        modalPortal.current.open();
        beginRecording(); // <-- also invoke
      } 
    : endRecording
  } 
>
  ... 
</button>
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Drew, I could kiss you. I've damn near thrown my laptop out the window over the last two hours. This well-voted answer (that suggests only ternaries can be used) was **very** misleading: https://stackoverflow.com/a/45573716/12137312 – lys Mar 09 '21 at 01:01
  • Ahh appreciate the edit to add more context to the answer. – lys Mar 09 '21 at 01:03
  • what I find infuriating is the the difference here between success and failure in the attempt I proposed is literally the brackets added to the function call. Just adding brackets to `beginRecording()` fixes it. – lys Mar 09 '21 at 01:07
  • 1
    @lys Interestingly enough, that other answer seems to address the linting warning/error, but it seems to be answering a different question/issue. To me it seems the mention of ternaries was an afterthought, but either way, I can see where that might lead you astray if you didn't realize what is implicitly returned for each case still needs to be valid by its own merit. – Drew Reese Mar 09 '21 at 01:09
  • 1
    @lys Right, syntax can be tricky (and important!) like that. – Drew Reese Mar 09 '21 at 01:12
  • I'm all for it! Thanks for providing insight. I guess the real issue here is not really comprehending the compiler error – lys Mar 09 '21 at 01:19
1

Try this:

const handleButtonClick = () => {
  if (isRecording) {
    endRecording()
    return
  }

  modalPortal.current.open()
  beginRecording()
}


<button onClick={handleButtonClick} />
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46