0

I am writing crud operations for my mern-stack using redux-toolkit. I want to prefill input fields with the data from the database. All that code works. I have the data in my input fields. But I am stopped by typescript, because TypeScript says:

The property X does not exist for the User[] type.

So how can I tell TypeScript that user has this properties? I tried it with exclamation marks and import the interface User into the userdisplay for setting user:User, but nothing helps.

The code in React:

const UserDisplay = () => {
    //bringing in the user
    const dispatch = useAppDispatch();
    const selector = useAppSelector((state:RootState)=>state.user);
    const {user, isError, isLoading, message} = selector;
    const {id} = useParams();
    useEffect(()=>{
        if(isError){
            toast.error(message);
        }
        dispatch(getUser(id!));
        return ()=>{
            dispatch(reset())
        }
    }, [dispatch, isError, message, id]);

      //set Variables for input fields
      const [formdata, setFormdata] = useState<{vorname:string, 
        nachname:string, username:string, email:string, street:string, 
       number:string,plz:string, city:string, isAdmin:boolean, 
       createdAt:string}>({
        vorname:"",
        nachname:"",
        username:"",
        email:"",
        street:"",
        number:"",
        plz:"",
        city:"",
        isAdmin:false,
        createdAt:Date.toLocaleString(),
    })
    console.log(user);
    const {vorname, nachname, username, email, street, number, plz, city, isAdmin, createdAt} = formdata;

   //overgive data from db to input fields
    useEffect(()=>{
        if(user){
            setFormdata({
                vorname:user.vorname, //here is every value underlined, property not exist
                nachname:user.nachname,
                username:user.username,
                email:user.email,
                street:user.street,
                number: user.number,
                plz:user.plz,
                city:user.city,
                isAdmin:user.isAdmin,
                createdAt:user.createdAt,
            })
        }
    }, [user])

That are the parts out of the slice:

interface User{
  _id?:string,
  id?:string,
  vorname:string
  nachname:string
  username:string
  email:string
  street:string
  number:string
  plz:string
  city:string
  password:string
  isAdmin:boolean
  createdAt: Date
  accessToken: string;
}
interface InitialState{
    user:User[],
    isLoading:boolean,
    isSuccess:boolean,
    isError:boolean,
    message:string,
}
const initialState:InitialState ={
    user:[],
    isLoading:false,
    isSuccess:false,
    isError:false,
    message:"",

}

type AsyncThunkConfig = {
    state: RootState
}
//this function works - I get the user in my userDisplay
export const getUser = createAsyncThunk<User[], string, AsyncThunkConfig>('/user/find', async (Id:string, thunkAPI)=>{
    try{
        const token = thunkAPI.getState().auth.user!.accessToken;
        return await userService.getUser(Id, token);
    }catch (error:any) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString()
      return thunkAPI.rejectWithValue(message as string)
    }
})

Slice:

export const userSlice = createSlice({
name: 'user',
initialState,
reducers:{
    reset:(state)=>{
        state.isLoading = false;
        state.isSuccess = false;
        state.isError = false;
        state.message = "";
    }
},
extraReducers(builder) {
  builder
  .addCase(updateUser.pending, (state)=>{
    state.isLoading = true;
  })
  .addCase(updateUser.fulfilled, (state, action)=>{
    state.isLoading = false;
    state.isSuccess = true;
    state.user.push(action.payload)
  })
  .addCase(updateUser.rejected, (state, action:any)=>{
    state.isLoading = false;
    state.isError = true;
    state.message = action.payload;
  })
  .addCase(deleteUser.pending, (state)=>{
    state.isLoading = true;
  })
  .addCase(deleteUser.fulfilled, (state, action)=>{
    state.isLoading = false;
    state.isSuccess = true;
    state.user.filter((item)=>item._id !== action.payload.id)
  })
  .addCase(deleteUser.rejected, (state, action:any)=>{
    state.isLoading = false;
    state.isError = true;
    state.message = action.payload;
  })
  .addCase(getUser.pending, (state)=>{
    state.isLoading = true;
  })
  .addCase(getUser.fulfilled, (state, action)=>{
    state.isLoading = false;
    state.isSuccess = true;
    state.user = action.payload; //that is underlined when I only use User or say object
  })
halfer
  • 19,824
  • 17
  • 99
  • 186
Roman
  • 137
  • 1
  • 14
  • There'll be a TypeScript way of doing this. Can you do `const {user, isError, isLoading, message} = selector` when `user` is instantiated? – halfer Nov 14 '22 at 23:26
  • @halfer, thanks for your response. I tried, what you suggested and get the following errors: The JSX element "User" does not have a corresponding closing tag.ts(17008) "User" refers to only one type, but is used here as a value.ts(2693) – Roman Nov 16 '22 at 11:22
  • Hmm, is it misinterpreting my `` cast as a JSX element? You might have to fiddle around with this to get it to work, but maybe instead try `const {user as User, isError, isLoading, message} = selector`. – halfer Nov 16 '22 at 22:54
  • 1
    Ooh, see [this question](https://stackoverflow.com/questions/40819973/typescript-type-casting-when-destructuring) as well. – halfer Nov 16 '22 at 22:55
  • Finally, make sure that the `User` type that is in scope at this point is the one you actually expect - the error indicates that the transpilers knows about a type of this name, but it might be looking at a different one. – halfer Nov 16 '22 at 22:57

0 Answers0