qwik icon indicating copy to clipboard operation
qwik copied to clipboard

Child component not re-rendering when passed `props.property` assigned to `const`

Open jessezhang91 opened this issue 3 years ago • 4 comments

Qwik Version

0.13.3

Operating System (or Browser)

OSX, Chrome

Node Version (if applicable)

No response

Which component is affected?

Qwik Runtime

Expected Behaviour

See: https://stackblitz.com/edit/qwik-starter-jdxoxt?file=src%2Froutes%2Findex.tsx

Given these three components:

export const ItemValueComponent = component$<{ item: string }>(({ item }) => {
  return <span>{item}</span>;
});

export const ItemComponent = component$<{ item: string }>((props) => {
  const item = props.item;
  return (
    <div>
      Expected: {item}, Actual: <ItemValueComponent item={item} />
    </div>
  );
});

export const ItemComponent2 = component$<{ item: string }>(({ item }) => {
  return (
    <div>
      Expected: {item}, Actual: <ItemValueComponent item={item} />
    </div>
  );
});

I expect the child components of ItemComponent and ItemComponent2 to render updated values whenever ItemComponent and ItemComponent2 receive new values in props.

Actual Behaviour

ItemComponent's inner ItemValueComponent does NOT update when the value in props changes.

ItemComponent2's inner ItemValueComponent does update when the value in props changes.

Additional Information

No response

jessezhang91 avatar Nov 13 '22 06:11 jessezhang91

Verified that this is an issue from 0.10.0, 0.11.1, and 0.12.1. I'm not sure if it goes even further back.

jessezhang91 avatar Nov 13 '22 06:11 jessezhang91

Simple workaround for now is to not reassign properties from props and pass them directly into the child components instead.

jessezhang91 avatar Nov 13 '22 07:11 jessezhang91

Agreed this should always work! however qwik wants to avoid rendering the parents to pass data, in your example:

  const item = props.item;

this is anti-pattern in qwik, since it needs to forces a rerender of the component just to pass data around. Agreed this is an issue though, i will look into it

manucorporat avatar Nov 13 '22 12:11 manucorporat

Fixed in 0.14.1!

manucorporat avatar Nov 28 '22 14:11 manucorporat

Just wanted to point out here that this seems to still happen with routeActions$:

// index.tsx
export const randomServerFetchAction = routeAction$(
  async (data, requestEvent) => {
    // This will only run on the server when the user submits the form (or when the action is called programatically)

    const userID = 0.53543;
    const waitFor = (delay: number) =>
      new Promise((resolve) => {
        setTimeout(resolve, delay);
      });

    await waitFor(1000);

    return {
      success: true,
      userID,
    };
  }
);

export default component$(() => {
  const action = randomServerFetchAction();
  let teststring = action.value?.userID;

  return (
    <div class="container">
      <h3>Res {action.value?.userID}</h3>
      <h3>Res1 {teststring}</h3>
      <Box text={action.value?.userID} />
      <Box text={teststring} />
      <Form action={action}>
        <button type="submit">Mock to Server</button>
      </Form>
    </div>
  );
});


// Box.tsx
interface BoxProps {
  text?: number;
}

const Box = component$((props: BoxProps) => {

  return (
    <>
        <p>{props.text} is the result</p>
    </>
  );
});

andrewhylee avatar May 02 '23 04:05 andrewhylee