OiO.lk Blog javascript Unpredictable variable context in Typescript classes
javascript

Unpredictable variable context in Typescript classes


I am trying to create a class in Typescript (React Native) that manages modals in a stack as to not have the modals consume too much memory (use case might not be super important). I am declaring an instance of the class and then pushing and popping from the class. However, when I call the push and pop methods, the stack contents are never what I expect. The ‘this" keyword is never pointing to the correct instance of the class…

I have done some reading up on classes in typescript and followed every suggestion I have found so far. Maybe I am trying to use typescript as if is java and that’s my problem…

Here is my ModalManager class:

type modalData = {
    modalFunction: Function; //Function that loads the modal
    modalParameters: Array<any>; //stack of paramers for function
}
  
class ModalManager {
    private modalMap: Map<string, modalData> = new Map<string, modalData>();
    private defaultView: ReactNode = <View></View>;
    private modalStack: Stack<string> = new Stack<string>([""]);
    private renderCallback: Function;
  
    constructor(renderCallback: Function) {
      this.renderCallback = renderCallback;
      this.setDefaultView = this.setDefaultView.bind(this);
      this.registerModal = this.registerModal.bind(this);
      this.pushModal = this.pushModal.bind(this);
      this.popModal = this.popModal.bind(this);
      this.render = this.render.bind(this);
    }
  
    setDefaultView(defaultView: ReactNode){
      this.defaultView = defaultView;
    }
  
    registerModal(modalName: string, modalFunction: (params: any[], containerModal: ModalManager) => ReactNode){
        if (modalName != "") {
            this.modalMap.set(modalName, {modalFunction: modalFunction, modalParameters: new Array()} as modalData);
        }
    }
  
    pushModal(modalName: string, modalParameters: any){
      //Make sure modal is registered
      if (this.modalMap.has(modalName)) {
        //put modal at top of modal stack
        this.modalStack.push(modalName);
        console.log("1", this.modalStack.peek())
        //put modal parameters in modal parameters stack
        this.modalMap.get(modalName)?.modalParameters.unshift(modalParameters);
        //Cause rerender
        this.renderCallback(modalName);
      }
    }
  
    popModal(){
        console.log("4", this.modalStack.peek())

        if (this.modalStack.peek() != "") {
            this.modalStack.pop()
            console.log("3", this.modalStack.peek())

            this.renderCallback(this.modalStack.peek())
        }
    }
  
    render(currentModal: string){
        console.log("2", this.modalStack.peek())

      return (
        <View>
          <View>
          {(currentModal != "") ? 
            this.modalMap.get(currentModal)?.modalFunction(this.modalMap.get(currentModal)?.modalParameters, this) : <View/>
          }
          </View>
          {this.defaultView}
        </View>
      );
    }
  }

Here is my stack class:

class StackNode<T> {
    next: StackNode<T> | null = null;
    value: T;

    constructor(value: T, next: StackNode<T> | null) {
        this.value = value;
    }
}


class Stack<T> {
    private top: StackNode<T> | null = null;

    constructor(starter: Array<T>) {
        this.push = this.push.bind(this);
        this.pop = this.pop.bind(this);
        this.peek = this.peek.bind(this);

        starter.forEach((item) => {
            this.push(item);
        })
    }

    push(value: T){
        let newTop: StackNode<T> = new StackNode<T>(value, this.top);
        this.top = newTop;
    }

    pop(){
        if (this.top != null) {
            this.top = this.top.next;
        }
    }

    peek(){
        return this.top?.value;
    }
}

And here is my app code:

const [currentModal, setCurrentModal] = useState("");

  const containerModal: ModalManager = new ModalManager(setCurrentModal)

  containerModal.setDefaultView(
    <View>
      <Button title="Open modal 1" onPress={() => {containerModal.pushModal("test", {name: "Eli", value: 10, key: "secret"})}} />
    </View>
  );

  containerModal.registerModal("test", testModalFunction);
  containerModal.registerModal("test2", test2ModalFunction);

  return (
    <View>
      {containerModal.render(currentModal)}
    </View>
  );
};

I have tried changing my methods from this style: () => {}, to the style it is now. I have tried to bind methods that you see present in the code. I really am scratching my head on this one, it seems like a pretty basic thing I’m trying to do.

Thanks for reading if you made it this far.

edit I suspect that it could be due to my render function where I am passing "this" as a parameter to the modal builder function. The modal then calls methods on the "this" that is passed to it and that’s where I’m getting bad behavior. Still not sure how to fix it…



You need to sign in to view this answers

Exit mobile version