Issue with updating state in child component preventing addition to state

Recently, I made the switch to TypeScript in my NextJS project using Create T3 App. One of the components in my app involves updating the state after a Prisma mutation is performed.

I attempted to pass the setItems (which was initialized with useState) to a child component, but encountered an error:

Type error: Argument of type '(prev: Example[]) => (Example | undefined)[]' is not assignable to parameter of type 'SetStateAction<Example[]>'.

Here is the code snippet causing the issue:

interface FormProps {
  setItems: Dispatch<SetStateAction<Example[]>>;
}

const Form: FC<FormProps> = ({ setItems }) => {
  const [message, setMessage] = useState("");
  //setItems([])
  const { mutate: createExample } = api.example.createExample.useMutation({
    onSuccess: (data) => {
      setItems((prev) => [...prev, data]);
    },
  });

  return (
    <form
      className="flex gap-2"
      onSubmit={(event) => {
        event.preventDefault();
        createExample({
          text: message,
        });
        setMessage("");
      }}
    >
      <input
        type="text"
        placeholder="Your message..."
        minLength={2}
        maxLength={100}
        value={message}
        onChange={(event) => setMessage(event.target.value)}
      />
      <button
        type="submit"
        className="rounded-md border-2 border-zinc-800 p-2 focus:outline-none"
      >
        Submit
      </button>
    </form>
  );
};

This is how I initialized setItems on my main page:

const Demo: NextPage = () => {
  const [items, setItems] = useState<Example[]>([]);
  const { data: session, status } = useSession();

  const user = api.example.getAll.useQuery(["getAll"], {
    onSuccess: (user) => {
      setItems(user.examples);
    },
  });

  const { mutate: deleteExample } = api.example.deleteExample.useMutation({
    onSuccess(example) {
      setItems((prev) => prev.filter((item) => item.id !== example.id));
    },
  });

  return (
    <>
      {session ? (
        <>
          <p>Hello {session ? session.user?.name : null}</p>
          <button
            type="button"
            onClick={() => {
              signOut().catch(console.log);
            }}
          >
            Logout
          </button>
          <br />
          <ul>
            {user.data
              ? items.map((example) => (
                  <li>
                    {example.text}{" "}
                    <button
                      onClick={() => {
                        deleteExample({ id: example.id });
                      }}
                    >
                      X
                    </button>
                  </li>
                ))
              : null}
          </ul>
          <br />
          <Form setItems={setItems} />
        </>
      ) : (
        <button
          type="button"
          onClick={() => {
            signIn("email").catch(console.log);
          }}
        >
          Login with Email
        </button>
      )}
    </>
  );
};

The function correctly recognizes the type as 'Example', but throws an error due to accessing the previous state. How can I merge states while maintaining consistent types?

Answer №1

It seems like the issue arises from the data variable possibly being undefined within the onSuccess method.

When TypeScript encounters a situation where an array with potentially undefined values is assigned to an array expected to have defined values, it raises an error.

To address this problem, we need to consider why data could be undefined. There are three scenarios:

  1. data consistently remains undefined (check if hovering over data shows data: undefined).

If this is the case, there may be a separate bug causing data to be undefined, or you actually intend for the list to contain undefined elements. In the latter scenario, inform TypeScript by adjusting useState<Example[]> to

useState<(Example|undefined)[]>
, and
Dispatch<SetStateAction<Example[]>>
to
Dispatch<SetStateAction<(Example|undefined)[]>>
.

  1. data occasionally includes undefined (data: Example|undefined).

If data can sometimes be undefined, two possibilities arise – either you want to include undefined in the list regardless, or you prefer to exclude undefined values.

If including undefined is desired, follow the previous step.

If excluding undefined is preferred, ensure that it is not undefined before executing the setItems function:

    onSuccess: (data) => {
      if (data)
        setItems((prev) => [...prev, data]);
    },

If the error persists despite these efforts, make the adjustment: [...prev, data as Example]

  1. Data is guaranteed never to be undefined.

If you are certain that data will always have a value and cannot be changed, utilize the approach from the second scenario to clarify this to TypeScript:

setItems((prev) => [...prev, data as Example]);

By addressing one of these cases, the issue should be resolved.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Display a Material UI SnackBar when an error occurs in the Mutation

I am currently using the Snack bar feature from the Materia-UI page, specifically the Customized SnackBars example. const variantIcon = { success: CheckCircleIcon, warning: WarningIcon, error: ErrorIcon, info: InfoIcon, }; const styles1 = theme = ...

What is the reasoning behind next.js requiring the use of modular CSS instead of a global stylesheet linked to each page?

I am currently attempting to include a minified link stylesheet created with sass:watch into a particular page of a next.js (13) project. However, upon adding it using the head component, I received this warning: Do not add stylesheets using next/head I ...

Resolving URL with Next.js context

Whenever I attempt to retrieve the URL on the server side using const { resolverUrl } = context, the URL includes characters '+' that are transformed into '%20' Is there a way to obtain the URL on the server side without this transfor ...

Using Rxjs to handle several requests with various headers

I have a specific requirement where, if hasProcessado == true, 10 additional requests should be made before issuing the final request. If the final request fails, 3 more attempts are needed. Furthermore, when sending the last request, it is essential to n ...

Looking to customize scrolling behavior when navigating back in Next.js?

I have a function in my index.js file that fetches a list of posts like this: const Index = (props) => { return ( <div> {props.posts.map((each) => { return ( <Link scroll={false} as ...

What is the process of declaring a global function in TypeScript?

I am looking to create a universal function that can be accessed globally without needing to import the module each time it is used. The purpose of this function is to serve as an alternative to the safe navigation operator (?) found in C#. I want to avoi ...

Is it possible to show an image without altering the Box dimensions?

Hi there, I am currently working on designing a footer and I have encountered an issue. I want to add an image in a specific position, but when I do so, it affects the size of the box. I was wondering if there is a way to set the image as a background or b ...

Difficulty encountered when trying to apply a decorator within a permission guard

I'm a newcomer to Nestjs and I am currently working on implementing Authorization using Casl. To achieve this, I have created a custom decorator as shown below: import { SetMetadata } from '@nestjs/common'; export const Permission = (acti ...

How to Modify CSS in Angular 6 for Another Element in ngFor Loop Using Renderer2

I have utilized ngFor to add columns to a table. When a user clicks on a <td>, it triggers a Dialog box to open and return certain values. Using Renderer2, I change the background-color of the selected <td>. Now, based on these returned values, ...

Trying to utilize RegEx for my project, but feeling stuck on how to solve my problem

^\d{1,12}$|(?=^.{1,15}$)^\d+\.\d{1,2}$ This is the current regular expression I am using. I need to adjust the maximum limit to 100,000,000,000 with an option for two decimal places. Additionally, I would like users to be able to inpu ...

"Implementing a onClick method to render a component in React with passed props – a complete

Is there a way to properly update the LatestTweetsComponent with data fetched in handleRequest? The `tweets` are correctly updating onClick, but the LatestTweetsComponent is not rendering or updating as expected. It seems like my current approach might b ...

What is the top choice for building a website with real-time capabilities?

When it comes to displaying real-time data with heavy UI/backend rendering, the choice of framework or language can make a big difference. In my recent experience using the MEAN stack with Angular 2, I found the constant need for two-way data binding and ...

Events trigger React to render multiple times

I have implemented socket functionality on my website where users can send a word to the server, triggering an event (art-addpic) that broadcasts an image URL corresponding to that word to all users. However, only users with isArtist=true are allowed to re ...

Using TypeScript with Node.js: the module is declaring a component locally, but it is not being exported

Within my nodeJS application, I have organized a models and seeders folder. One of the files within this structure is address.model.ts where I have defined the following schema: export {}; const mongoose = require('mongoose'); const addressS ...

Mastering the process of importing AngularJS submodules in TypeScript

Currently, I am in the process of structuring an AngularJS (Angular 1) project using TypeScript. To compile TypeScript & ES6 to JavaScript, I have set up webpack. In my webpack configuration, I only compile the "app.ts" file and any other files it imports ...

Animation of scroll progress in Next.js and Framer Motion

I am attempting to create a viewport scroll progress circle using Framer Motion in a Next.js application, incorporating Tailwind CSS. I referred to the example code provided by Framer Motion here:https://codesandbox.io/s/framer-motion-viewport-scroll-and-s ...

Having trouble with Materialize Tabs in React Router?

Can anyone help me understand why Materialize tabs are not functioning properly when used with react router? In my project, I have a navigation bar with three options: "Hotels", "Statistics", and "Academy". Each of these options leads to a respective page ...

Improving conditional rendering in Mui <Cards> component by fixing state behavior

I have a situation where I want to display a Floating Action Button inside each Mui card when hovered over. However, I'm running into an issue with the hover state affecting all cards instead of just the one being interacted with. How can I ensure tha ...

Pause the React rendering process to initiate a redirection

Currently, I am developing a React application using Next.js and facing a specific challenge that requires a solution: There are certain pages in my application that should only be accessible once a particular context has been established. To achieve this ...

Tips for incorporating constants and functions into a React Component class

Looking at the AppBar code from Material-UI: https://material-ui.com/components/app-bar/#app-bar-with-a-primary-search-field I came across a section called "renderMobileMenu" which I want to integrate into my React Component class. However, the sample cod ...