javascript - Referens till "this" är null i funktion som anropas av redux-saga call


Jag lär mig redux-saga och jag försöker integrera det i ett projekt som använder ett API som genereras med openapi-generator som producerar utdata som följande:

async loginUser(body: Login): Promise<LoginResponse> {
    debugger;
    const response = await this.loginUserRaw({ body: body });
    return await response.value();
}

Och loginUserRaw är en funktion som utför den faktiska inloggningen. Sedan har jag följande saga:

function* login(action:Login) {
    try{
        const response = yield call(User.loginUser, action);
        yield result(LOGIN_SUCCESS, response)
    }catch(e){
        yield result(LOGIN_FAILURE, e)
    }
}

När detta körs får jag ett fel i min API-metods await this.loginUserRaw({ body: body }); rad:

TypeError: Cannot read property 'loginUserRaw' of null

Jag felsöker det och ser att this är null. När jag binder funktionen uttryckligen i sagan:

const response = yield call(User.loginUser.bind(User), action);

Det fungerar, men jag vill inte binda det varje gång jag anropar en funktion. Hur kan jag få saga att fungera utan att uttryckligen binda funktionen? (Jag kan inte heller ändra den genererade koden och ta bort this)

Svar #1

Kontexten i Javascript är dynamisk baserat på hur du har skrivit din kod

const loginUser = User.loginUser
loginUser() // context lost, because there is no longer `User.` in front of it

Samma gäller när du skickar en funktion som en parameter. Detta innebär att du måste tillhandahålla call-effektens kontext på något sätt. bind-metoden är ett alternativ, men effekten själv stödjer flera sätt att tillhandahålla kontext. Du kan hitta dem i de officiella dokumenten https://redux-saga.js.org/docs/api/#callcontext-fn-args, här är den korta versionen av det:

Möjliga sätt att vidarebefordra kontext:

call([context, fn], ...args)
call([context, fnName], ...args)
call({context, fn}, ...args)

Så till exempel kan du göra följande:

const response = yield call([User, User.loginUser], action);

Svar #2

Ett annat alternativ för att lösa detta problem är att konvertera dina klassmetoder till arrow-funktioner. Detta kommer att säkerställa att this korrekt refererar till klassinstansen.

class UserApiDomain {
  loginUser = async (body: Login): Promise<LoginResponse> => {
    debugger;
    const response = await this.loginUserRaw({ body: body });
    return await response.value();
  }

  loginUserRaw = async ({ body }: YourRawType): Promise<YourRawResponse> => {
    ....
  } 
}

Genom att använda pilfunktioner kommer this-kontexten alltid att referera till instansen av UserApiDomain, vilket förhindrar felet TypeError: Cannot read property 'loginUserRaw' of null.

Relaterade artiklar