How to pass functions as props in Qwik JS?

2.1k Views Asked by At

Problem

In React, we can pass functions as props into a child component when a function requires access to a state within the parent component of the aforementioned child. I was writing code for an application where I need such behavior to be implemented. However, I'm having trouble finding proper conventions for defining functions in Qwik, and then sending them through.

Attempt

I've tried defining the function within my interface to see if that helps Qwik to allow this implementation but so far that has not worked either.

Code

I'm trying to launch a modal from a icon contained in the header within my application. I'm trying to control displaying the modal by using a store declared within my header component. It's a Boolean value and determines if the modal would be displayed or not. I defined the function for modifying the state within my header component and attempted to pass it into my modal child component.

// components/header/header.tsx
import { component$, useClientEffect$, useStore } from "@builder.io/qwik";
import { strictEqual } from "assert";
import Modal from "../modal/modal";

export default component$(() => {
    const store = useStore({
        ...
        modal: false
    });

    function onClose() {
        store.modal = false;
    }

    return (
        <header>
            {store.modal && <Modal onClose={onClose}/>}
            <div 
                onClick$={()=>{
                    store.modal = true;
                }}
            >
                <i class="fa-solid fa-cart-shopping"></i>
            </div>
        </header>
    );
});

Inside my modal component I tried to use an interface to indicate that I'm passing a function into my props and tried to set as the function to execute within another icon contained within my child component.

// components/modal/modal.tsx
import { component$ } from "@builder.io/qwik";
import { strictEqual } from "assert";

interface ModalProps {
    onClose: () => void
}

export default component$((props: ModalProps) => {
    return (
        <div>
            <div>
                <h1>Modal</h1>
                <i onClick$={props.onClose}></i>
            </div>
        </div>
    );
});

Error Message

When I click on the icon within my header, it displays the following error in my terminal.

log.js:10 QWIK ERROR Error: Code(3): Only primitive and object literals can be serialized at Array.flatMap (<anonymous>) ƒ onClose() { store.modal = false; }

Conclusion

Is there anyway to send functions as props into child components in Qwik JS?

If not, can I access stores contained in a parent component from within a child component?

Basically, what would be the ideal approach to solve this issue?

3

There are 3 best solutions below

1
On BEST ANSWER

As I'm a noob like you in this framework, I've struggled to understand how this works too.

You actually need to pass a QRL as you may read here:

https://qwik.builder.io/docs/components/events/

So, here's how to modify your code for the Modal component:

import { component$, QRL } from '@builder.io/qwik';

interface ModalProps {
    onClose: QRL<() => void>;
}

export default component$<ModalProps>(props => {
    return (
        <div>
            <div>
                <h1>Modal</h1>
                <i onClick$={props.onClose}></i>
            </div>
        </div>
    );
});

And your head component:

import { $, component$, useStore } from '@builder.io/qwik';

import Modal from '../components/test';

export default component$(() => {
    const store = useStore({
        modal: false
    });

    const onClose = $(() => {
        store.modal = false;
    });

    return (
        <header>
            {store.modal && <Modal onClose={onClose} />}
            <div
                onClick$={() => {
                    store.modal = true;
                }}
            >
                <i class="fa-solid fa-cart-shopping"></i>
            </div>
        </header>
    );
});
1
On

For me, I'm using PropFunction type to define the function prop. For example

import { type PropFunction } from "@builder.io/qwik";

and here is a full example of how I pass onClick$ event as a prop

import { component$, type PropFunction } from "@builder.io/qwik";

interface Props {
  onClick$?: PropFunction<() => void>;
}

export const Button = component$((props: Props) => {
  return <button onClick$={props.onClick$}>Click me!</button>;
});
0
On
import type { PropFunction } from '@builder.io/qwik';
import { component$ } from '@builder.io/qwik';
import type { Signup } from '~/routes/login';

export const ButtonDefault = component$((props: { name: string, link?: string, signup?: Signup, setSignUpState$?: PropFunction<(signup: Signup) => void> }) => {
  
  return (
    <button onClick$={()=>{if(props.setSignUpState$ && props.signup){props.setSignUpState$(props.signup)}}} class="dark:darker-light dark:hover:lighter-light lighter-dark hover:darker-dark font-bold py-2 px-4 rounded" type="button">
      {props.name}
    </button>
  );
});

i can pass the function as props to my template as below

export type Signup = { signup: boolean };
const signup = useSignal<Signup>({ signup: false });
<ButtonDefault name={'Login'} signup={signup.value}  setSignUpState$={(signup: Signup) => { signup.signup = false;}} />