preactjs / preact-compat

ATTENTION: The React compatibility layer for Preact has moved to the main preact repo.
http://npm.im/preact-compat
MIT License
949 stars 148 forks source link

Missing createContext #475

Closed Pyrolistical closed 5 years ago

Pyrolistical commented 6 years ago

React libraries that use the new Context API will have import {createContext} from 'react', and that doesn't work with preact-compat

Should we just re-export https://github.com/valotas/preact-context while we wait for createContext to be added to preact https://github.com/developit/preact/pull/963?

kontrollanten commented 6 years ago

I'm missing createContext too. @Pyrolistical, what do you mean with "re-export"? I'm looking for a workaround until we've something merged in preact or preact-compat.

mindplay-dk commented 6 years ago

I didn't know preact-context existed!

The fact that this exists really should be documented somewhere in plain sight.

I was beginning to worry that Preact and React were about to diverge too substantially with the introduction of the new context API in React - and also have been looking for a simple solution for application-wide shared state. This looks great!

developit commented 6 years ago

There's some funky bitmask stuff in preact-context that I'm not super sure we need in preact-compat (it doesn't seem to be part of the officially supported React API). Perhaps we could just add something like this?

function createContext(initialValue) {
  const key = `$ctx-${++counter}`;
  class Provider {
    getChildContext() {
      let ctx = this.ctx || (this.ctx = {});
      if (ctx[key]!==this.props.value) {
        for (let i=0; i<subs.length; i++) subs[i].setState(null);
      }
      ctx[key] = this.props.value;
      return ctx;
    }
    render(props) {
      return props.children[0];
    }
  }
  class Consumer extends Component {
    componentDidMount() {
      consumers.push(this);
    }
    componentWillUnmount() {
      consumer.splice(consumers.indexOf(this), 1);
    }
    render(props, state, context) {
      return this.props.children[0](key in context ? context[key] : initialValue);
    }
  }
  return { Provider, Consumer };
}
kontrollanten commented 6 years ago

I've been thinking a bit about this while I've been working on different projects using both React and preact and I'm not sure that I really want this feature into preact anymore.

What I really fell for with preact/React is the inspiration of functional programming and it's "simplicity", in the meaning that without side effects it's really easy to isolate, debug and understand the web clients. Compared to for instance Angular it's, in my opinion, a huge difference in the complexity. My picture is that React now is moving away from this approach and starts to implement side effects in the framework (context), and what I've heard there's more new features to come.

Of course side effects can make web apps easier to build, and also remove complexity. But I think that with the tools to create side effects developers will tend to use those as shortcuts in cases where pure functional programming can solve the same issue. The price for the developers (and the projects) is technical debt.

With the background of this I can really see a purpose with preact not following this trend. I guess the purpose of creating a preact API similar to React was easy migration between those frameworks, but now preact is pretty big and maybe this can be a start for a diverge between those two frameworks where it's not longer obvious that preact should try to follow Reacts pattern.

What do you think?

mindplay-dk commented 6 years ago

@kontrollanten side-effects is not the only use for this - I'm using it simply as a means of sharing state between related components, e.g. a <TabList> with <Tab> panels, where the panels can share the selected tab state, which is provided by the parent tab-list. (even works fine for nested tabs.)

I guess the purpose of creating a preact API similar to React was easy migration between those frameworks, but now preact is pretty big and maybe this can be a start for a diverge between those two frameworks where it's not longer obvious that preact should try to follow Reacts pattern.

Isn't that why we have preact-compat?

@developit most likely we're talking about an addition to that, not an addition to the framework itself?

I think it's very important to have that feature in preact-compat - the whole point of that package existing, is to provide API-compatible React idioms, and I think there's no doubt the new context API is already a popular idiom in React?

Pyrolistical commented 6 years ago

yeah, the more I look at preact, the more I think preact should just do its own thing.

Even react regrets the move from createClass to es6 classes, so they are not infallible.

I would love to see preact do its own thing and continue with ideas like having props/state as params in render and simplifying children.

I want preact to be different enough that we won't expect compat at all

kontrollanten commented 6 years ago

@kontrollanten side-effects is not the only use for this - I'm using it simply as a means of sharing state between related components, e.g. a with panels, where the panels can share the selected tab state, which is provided by the parent tab-list. (even works fine for nested tabs.)

@mindplay-dk That's true, but I think my reasons applies for those cases also; it's no longer functional and my opinion is that this behaviour will in many cases increase application complexity.

Isn't that why we have preact-compat?

I was mainly thinking about implementing context API into preact, maybe I should've created this discussion on preact project but I guess that a decision like this will affect both preact and preact-compat.

One way could be to keep preact-compat up to date with latest React features and keep preact slimmed. I'd love a clear distinguish between those projects; where preact strives for a petite, functional rendering framework and preact-compat makes preact easier to use for those who needs React's features, being able to import them one by one.

developit commented 6 years ago

I'm partial to the idea of exporting createContext from preact-compat, and also shipping it as a standalone module (or just pointing to preact-context). It's another dependency for Preact core users, but I think most people actually prefer small composable dependencies at this point.

Going forward, I think the main metric we want to use for whether things get bundled into Preact itself is actually whether they can feasibly be implemented as standalone modules. Something like the new Context API is actually designed to be a standalone module, and nice implementations exist. Something like createRef() is a bit less clear-cut, since Preact could support it in ~40 bytes with the same semantics as React. Supporting createRef as an add-on module is smaller, but the value returned by createRef is a function rather than an Object.

As for preact-compat, I'd like to move the React 0.14.x features into a separate module, a "legacy" version, or just remove them entirely. List-based features like the React.DOM.div factories are just wasteful, and we're putting them into bundles knowing most people will never use them. I'd rather save those bytes for new features, or just give folks some bytes back :)

developit commented 5 years ago

For those finding this issue, please use styled-components@3 for now.

moshest commented 5 years ago

@developit I think that we should let the bundlers with the tree-shake functionality to save more bytes when removing createContext and other methods and focus on making this package simple and easy to use.

Right now I'm using the following "pollyfill" for React 16:

$ npm i create-react-context create-react-ref
// preact-compat.js
import React from 'preact-compat';

export default React;
export * from 'preact-compat';

const createContext = require('create-react-context');
const forwardRef = require('create-react-ref/lib/forwardRef');
const createRef = require('create-react-ref/lib/createRef');

export { createContext };

React.createContext = createContext;
React.forwardRef = forwardRef;
React.createRef = createRef;

Then, on webpack:

  resolve: {
    alias: {
      react: path.join(__dirname, 'preact-compat.js'),
      'react-dom': path.join(__dirname, 'preact-compat.js'),
    },
  }
marvinhagemeister commented 5 years ago

createContext is available in Preact X. An alpha is already out on npm. Even more exciting is that we moved compat into the core repo. Changing preact-compat to preact/compat in your require or import statements is all that's needed to be able to use createContext.

ko06 commented 5 years ago

Can we use the styled component 4.0.0 in preact?

@FYI, it working perfectly on 3.4.9.

can anyone suggest me the approach?

Screenshot 2019-08-10 at 6 42 28 PM Screenshot 2019-08-10 at 6 42 16 PM Screenshot 2019-08-10 at 6 41 48 PM

marvinhagemeister commented 5 years ago

For styled-components V4 to work with Preact you need to upgrade to the upcoming Preact version (currently in the release candidate phase).

ko06 commented 5 years ago

Is there anyway achieve in preat 8 version?

marvinhagemeister commented 5 years ago

No.