preactjs / preact

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
https://preactjs.com
MIT License
36.73k stars 1.95k forks source link

Context being hijacked by second instance of application #1998

Closed AlmeroSteyn closed 5 years ago

AlmeroSteyn commented 5 years ago

I am facing a problem in a current application we are building with Preact.

Firstly I have not been able to reproduce the issue in a small repo with one Preact app so I want to state it first to hear if anyone has any ideas.

Issue description

I have created a chat window web application that should be able to start on any web page by rendering the Preact application in a specific element.

This Preact application relies heavily on Context as well as the useContext hook.

The application is written purely in Hooks and function components.

There may more more than one chat window on a page, so this means starting more than one Preact application, each targeting another element.

In v10.0.0-rc3 everything works as expected.

However if I upgrade the to v10.0.0 launching a second application and rendering the useContext consumers seems to override the Context values provided by the Context.Provider in the first application.

After that the useContext hooks in the first application gets the Context values provided by the second application only.

If I revert to v10.0.0-rc3 again the two applications once again function properly as separate entities.

Are there any changes between 10.0.0-rc3 and 10.0.0 that could have triggered the issue I am seeing?

I will continue trying to reproduce the issue in a small repo, but any ideas while I am doing that would be extremely helpful as I have re reverse engineer quite a large app to find the exact point of failure.

JoviDeCroock commented 5 years ago

Hey,

You mean that the second applications context value is copied over to the first one? The only noticeable change that happened to context between rc.3 and stable is this one: https://github.com/preactjs/preact/pull/1931/files#diff-e959fc84d230f763daeb8f8fa0a571e7R27

Which should not have such an effect, it's pretty hard to put a label on it atm.

EDIT: all changes for anyone wondering https://github.com/preactjs/preact/compare/10.0.0-rc.3...10.0.0

AlmeroSteyn commented 5 years ago

@JoviDeCroock Yes that is exactly what seems to be happening.

The weird thing is the dispatch function from a useReducer hook, that is also going via context, stays correct, it just seems that the data passed via the providers are then copied over.

I will keep looking to see if I can reproduce it. I am not seeing anything wrong in the app at the moment (which does not mean that there isn't something) and changing back in forth just in the Preact version without any other code changes toggles between expected and broken function.

I will also look if copying the old code of the change above in solves it with 10.0.0 in place if that does not break things elsewhere.

JoviDeCroock commented 5 years ago

It is kind off odd that dispatch would be equal in that case since that is only set once https://github.com/preactjs/preact/blob/master/hooks/src/index.js#L80

AlmeroSteyn commented 5 years ago

@JoviDeCroock Thanks for the feedback.

After much trying I finally got to a minimal repo reproducing this issue.

https://github.com/AlmeroSteyn/preact-context-hijack

It seems that if I share a state variable from useState as the value of a Context Provider and inside the tree have a context consumer in the form of useContext AND I set state inside THAT component, the one instance of the application takes over the context value of the other.

The above repo uses yarn so can you try it with that just to ensure you get the same versions as in the yarn.lock ?

When you run the app with yarn server it will bootstrap a dev server on port 8080, if it is free.

You should see two inputs with text next to them. Top one says inline and the bottom window. These texts are passed through via Context and rendered.

image

Both inputs are separate Preact apps, bootstrapped and rendered into a specific element in the DOM.

When you start typing in the top input you will see that the text changes from inline to window. That is the Context Provider value of the app rendered at the bottom.

image

This only seems to happen when the Context Provider gets its state from a useState variable.

I hope that this gives enough info to trace the issue. It really does look as this is happening in Preact itself. If you downgrade the Preact version to v10.0.0-rc.3 this issue disappears.