klaro-org / klaro-js

Klaro Privacy Manager. An open-source, privacy-friendly & compliant consent manager for your website.
https://klaro.org
Other
1.18k stars 249 forks source link

After accepting purpose, klaro.show() shows purpose as not accepted (works only after page refresh) #334

Closed ConcurrentHashMap closed 3 years ago

ConcurrentHashMap commented 3 years ago

I found a weird behavior when using klaro and when adding a cookie button in the footer of my page.

Klaro Config

var klaroConfig = {
    storageName: 'CookieConsent',
    acceptAll: true,
    disablePoweredBy: true,
    hideToggleAll: false,
    htmlTexts: true,
    cookieExpiresAfterDays: 365,
    services: [
        {
            purposes: ['analytics'],
            name: "Test",
        },
    ]
};

Steps to reproduce

  1. Click "Accept" for the purpose Analytics
  2. Click onto custom "Cookie" button in the footer that calls klaro.show(undefined, true)

Current behavior

The recently accepted service is not shown as active in the opening modal. After refreshing the page, it is.

Expected behavior

The recently accepted service should directly be shown as active.

adewes commented 3 years ago

Hi @ConcurrentHashMap! Thanks for reporting this, can you explain in detail where you click on "Accept"? It seems you have the acceptAll flow enabled, so clicking on the "Accept all" button in the notice should enable all services. If you enable or disable a purpose or service in the modal you still need to save this by clicking on "Ok" (this is intentional), if you just exit the modal the service state will be reset to the previously chosen value. Might this be what you experience here?

ConcurrentHashMap commented 3 years ago

Hi @adewes I'm seeing the little overlay, asking me "Hallo! Könnten wir bitte einige zusätzliche Dienste für Analytics...". If I click "Das ist ok", the overlay disappears. In my footer is call klaro.show(undefined, true) afterwards, the modal comes up but the purpose I recently consented isn't show as active there (although the script is fired). When I use klaro.show(), it will show the expected results, but not with the klaro.show(undefined, true).

adewes commented 3 years ago

That's weird, the two function calls should do the same. Can you provide a link to your site or the source code so I can debug this further?

ConcurrentHashMap commented 3 years ago

Maybe it's a problem with using Next.js, because the cookie consent itself and the Cookie button calling klaro.show() are located in different components.

adewes commented 3 years ago

Ah, I see! Maybe you have imported Klaro twice? Klaro saves the default configuration into a global variable within its own library context, and also initializes consent managers within that context (as we didn't think about the case where Klaro would be loaded multiple times). This could indeed explain this behavior, as after a reload both Klaro versions will check the cookie for the consent data and display the correct data.

ConcurrentHashMap commented 3 years ago

Seems like this... How can I export this context from klaro to Next components?

At the moment I added the config and klaro as simple import in the <Head>

script defer type="text/javascript" src="/js/klaro.js" data-config="klaroConfig"></script>

Inside the components I tried accessing it with window.klaro.

adewes commented 3 years ago

Hm, the consent managers are stored in an object that lives in the Klaro library context and is not exported currently, you can get a consent manager using the getManager function but you can't set it. Is it really necessary to load Klaro twice? In general I'd recommend loading it only once in a given window, as it's really not designed to be executed in parallel from different context, so there might be other bugs you encounter when doing that.

ConcurrentHashMap commented 3 years ago

So what would you recommend regarding the use inside a Nextjs/react project? Is there a possibility to export klaro as a global singleton object that can be accessed from window?

At the moment I didn't find any solution how to correctly integrate klaro... Examples would be very nice for that.

adewes commented 3 years ago

Klaro should work really well with Next.js since it's written in React as well. I'd recommend importing it via npm and using the source files directly. We have an example for loading klaro via Webpack in the examples directory (https://github.com/kiprotect/klaro/tree/master/examples/klaro-and-webpack), though we use the dist bundle there.

To import Klaro from source you can just write

import * as Klaro from "klaro/src/klaro"

This will automatically call the setup() method that will show Klaro. If you don't want that, simply import the functions from klaro/src/lib and perform the setup yourself.

To access Klaro from the window context you could simply assign it manually via NextJS.

To give more specific advice I'd need to understand what your goals are. I think you want to embed Klaro in a Next.js app but also place a link outside of your applications' DOM that opens the consent modal? Anything else you're trying to achieve?

ConcurrentHashMap commented 3 years ago

Hey, thank you for your comment.

I just want to use klaro directly inside Next/React. But importing like above gives me the error window is not defined How can I fix this when importing from npm? I think klaro should be easily usable like importing it and calling the setup from an useEffect hook, don't you?

Regarding the two embedded options: No, it's both inside the application's DOM, on time inside the , on time inside the

adewes commented 3 years ago

Ah I see, the problem might be that Next.js executes your code both on the server-side and the client-side, on the server-side window is undefined.

Did you try importing lib instead of klaro? lib shouldn't directly call anything that requires a window object, so you should be able to use that directly.

That said in my opinion it doesn't make sense to run Klaro in a context that doesn't provide a valid window object. I'm not an expert in Next.js but I think importing the klaro/src/lib module and calling setup from componentDidMount or a useEffect hook should do the trick! Let me know if that gives you trouble, I'll then try to set up a Next.js example project.

ConcurrentHashMap commented 3 years ago

I've setup a very basic project for you to try it out: https://github.com/ConcurrentHashMap/nextjs-klaro-example

When importing import * as Klaro from "klaro/src/lib" it gives me the following error:

/workspace/nextjs-klaro-example/node_modules/klaro/src/lib.js:3
import React from 'react'
^^^^^^

SyntaxError: Cannot use import statement outside a module
adewes commented 3 years ago

Hi @ConcurrentHashMap, you can e.g. use an asynchronous import statement to load Klaro at runtime in the browser, triggered by an useEffect hook:

import Layout from "../components/Layout"
import { useEffect, useState } from "react"

function HomePage() {
    const [ klaro, setKlaro] = useState(null)
    useEffect(()=>{
        import("klaro").then((klaro)=>setKlaro(klaro))
    })
    return (
        <Layout klaro={klaro}>
            <h1>Welcome to Next.js!</h1>
        </Layout>
    )
  }

export default HomePage

Here I passed the Klaro module to the Layout component via a useState hook, you could as well assign it to the window to make it available both from within your Next.js project and from the normal DOM.

I realized that importing the src modules via Webpack from the node_modules directory requires some modifications to your Webpack setup, as by default Webpack will not treat those files as ES6 modules. Hence it's easier to use the regular Klaro package. I added a check to the setup function that will not proceed if window is undefined, from the next version on Klaro should be importable without a valid window object.

ConcurrentHashMap commented 3 years ago

Thanks a lot for that, it works fine with importing it in the useEffect hook. Didn't come up with that idea!