简体   繁体   中英

Child is not updated when boolean prop is changed

I have the following components:

Parent:

<template>
    <Child path="instance.json" 
             v-bind:authenticated="authenticated" 
             v-bind:authenticator="authenticator" 
    />
</template>

<script>
import { getAuthenticator } from '../auth';
export default {
  data() {
    return {
      authenticated: false,
      authenticator: null
    };
  },
  beforeMount: async function () {
    this.authenticator = getAuthenticator()
    this.checkAccess();
  },
  methods: {
    checkAccess() {
      this.authenticated = this.authenticator.isAuthenticated();
    },
    async login() {
      this.checkAccess();
      await this.authenticator.signIn();
      this.checkAccess();
    }
  }
};
</script>

Child:

<template>
  <div id="swagger-ui"></div>
</template>

<script>
import swagger from "swagger-ui-dist";
import "swagger-ui-dist/swagger-ui.css";

export default {
  props: ["path", "authenticated", "authenticator"],
  mounted: async function() {
    if (this.authenticated) {
      let token = (await this.authenticator.getToken()).accessToken;

      const ui = swagger.SwaggerUIBundle({
        url: this.path,
        dom_id: "#swagger-ui",
        onComplete: function() {
          ui.preauthorizeApiKey("token", token);
        }
      });
    } else {
      const ui = swagger.SwaggerUIBundle({
        url: this.path,
        dom_id: "#swagger-ui"
      });
    }
  }
};
</script>

In the parent component, when the login method is called, the authenticated variable changes to true . Since authenticated is passed as a prop to the Child component, I'd expect the Child to be refreshed whenever authenticated is changed. However, the Child does not refresh.

I think that the problem might be caused by the fact that I am not using authenticated in the template of the child at all. Instead, I'm using it only in the mounted hook. In my case, I have no use for authenticated in the template.

I tried two solutions:

  • calling this.$forceUpdate() in the login method of Parent - that didn't work at all (nothing changed)
  • Adding :key to the Child, and changing the key each time the login is called - this works, however, it's a bit hacky. I'd like to understand how to do that properly.

It's fine that you're not using it in the template, the issue is that you only check authenticated in the child's mounted hook, which only runs once (and is false at that time).

You should use a watch to track changes to the authenticated prop instead of mounted :

watch: {
  authenticated: {
    handler(newValue, oldValue) {
      this.setUi();
    },
    immediate: true   // Run the watch when `authenticated` is first set, too
  }
}

That will call a setUi method every time authenticated changes:

methods: {
  async setUi() {
    if (this.authenticated) {
      let token = (await this.authenticator.getToken()).accessToken;

      const ui = swagger.SwaggerUIBundle({
        url: this.path,
        dom_id: "#swagger-ui",
        onComplete: function() {
          ui.preauthorizeApiKey("token", token);
        }
      });
    } else {
      const ui = swagger.SwaggerUIBundle({
        url: this.path,
        dom_id: "#swagger-ui"
      });
    }
  }
}

what you need is to use a watcher. Actually, your code is only run once (when de component is mounted), not at each prop change.

<template>
  <div id="swagger-ui"></div>
</template>

<script>
import swagger from 'swagger-ui-dist';
import 'swagger-ui-dist/swagger-ui.css';

export default {
  props: {
    path: {
      type: String,
      default: '',
    },
    authenticated: {
      type: Boolean,
      default: false,
    },
    authenticator: {
      type: Object,
      default: () => {},
    },
  },
  watch: {
    async authenticated(newValue) {
      await this.updateSwagger(newValue);
    },
  },
  async mounted() {
    await this.updateSwagger(this.authenticated);
  }
  methods: {
    async updateSwagger(authenticated) {
      if (authenticated) {
        const token = (await this.authenticator.getToken()).accessToken;

        const ui = swagger.SwaggerUIBundle({
          url: this.path,
          dom_id: '#swagger-ui',
          onComplete: function () {
            ui.preauthorizeApiKey('token', token);
          },
        });
      } else {
        const ui = swagger.SwaggerUIBundle({
          url: this.path,
          dom_id: '#swagger-ui',
        });
      }
    },
  },
};
</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM