valotas / preact-context Goto Github PK
View Code? Open in Web Editor NEWReact new Context API for preact
License: Other
React new Context API for preact
License: Other
Preact X now supports the new Context API. Probably the main README of this library should mention that and maybe mention why or when would this library be useful.
When we use react we are able to do something like App.contextType = OurContext; And then in, for example, componentDidMount() write let someVar = this.context.someVarFromContext to get last value of someVarFromContext from imported context.
How can I get the same - this.context.someVarFromContext using preact-context? Or is it not implemented?
Hi,
I'm trying to build the library(npm i && npm run build
) but I'm getting the following error:
> tsc -p .
src/context.ts:27:23 - error TS7017: Element implicitly has an 'any' type because type 'string | number | object | VNode<any> | ComponentChild[]' has no index signature.
27 return (children && children[0]) || render;
~~~~~~~~~~~
src/context.ts:62:32 - error TS2339: Property 'length' does not exist on type 'string | number | object | VNode<any> | ComponentChild[]'.
Property 'length' does not exist on type 'number'.
62 if (children && children.length > 1) {
~~~~~~
src/context.ts:67:28 - error TS7017: Element implicitly has an 'any' type because type 'string | number | object | VNode<any> | ComponentChild[]' has no index signature.
67 return ((children && children[0]) || null) as JSX.Element;
I'm not that familiar with TypeScript but I would appreciate any help.
Thanks!
I am just trying to make demo working but it doesn't work.
import {h, Component} from 'preact';
import { createContext } from 'preact-context';
const SecureContext = createContext('password');
export default class Terms extends Component {
render() {
return <SecureContext.Provider value="hell world">
<h1>
<SecureContext.Consumer render={password => <span>Password is {{password}}</span>} />
Terms
</h1>
</SecureContext.Provider>;
}
}
password is empty and "Password is Terms" is generated.
No errors in js console.
build log is also fine.
"dependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/plugin-transform-react-jsx": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"@babel/preset-stage-2": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"babel-loader": "^8.0.5",
"preact": "^8.4.2",
"preact-context": "^1.1.2",
"typescript": "^3.2.2",
"webpack": "^4.28.1",
"webpack-cli": "^3.2.1"
},
"devDependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-react": "^7.0.0",
"preact-async-route": "^2.2.1",
"preact-router": "^2.6.1",
"tslint": "^5.12.0"
}
npm --version
6.5.0
16:02$ node --version
v10.14.2
16:02$ webpack --version
webpack: command not found
16:02$ webpack-cli --version
3.1.2
webpack config:
module.exports = {
entry: "./src/bootstrap.tsx",
output: {
filename: "./public/bundle.js",
publicPath: '/dist/'
},
mode: 'development',
// Enable sourcemaps for debugging webpack's output.
devtool: "eval",
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
rules: [
// Handle .ts and .tsx file via ts-loader.
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [['@babel/typescript', { jsxPragma: "h" }]],
plugins: [
['@babel/proposal-class-properties'],
['@babel/proposal-object-rest-spread'],
['@babel/plugin-syntax-dynamic-import'],
["@babel/transform-react-jsx", { "pragma": "h" }]
]
}
}
}
]
}
};
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": false,
"noEmit": true,
"strict": true,
"allowJs": true,
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react",
"jsxFactory": "h"
},
"include": [
"src"
]
}
I am trying to get a handle on how preact-context is used within a component ecosystem. Perhaps I am going about this wrong, but here is what I am attempting to play with:
First, I am trying to create a data provider which will (eventually) perform the server logins/logouts/tokens etc.
server.js
import { h, Component } from 'preact';
import { createContext } from 'preact-context';
const DataProviderContext = createContext();
const { Provider, Consumer } = DataProviderContext;
export default class DataProvider extends Component {
constructor() {
super();
}
state = {
url: 'http://localhost:3000',
username: 'testuser',
password: 'testpw'
};
render() {
return <Provider value={this.state}>{this.props.children}</Provider>;
}
}
Then, the consumer is the Login page which pulls in the default values and then can set them when the user makes the change.
login.js
import { h, Component } from 'preact';
import { createContext } from 'preact-context';
import style from './style';
import linkState from 'linkstate';
const DataProviderContext = createContext('DataProvider');
const { Provider, Consumer } = DataProviderContext;
export default class Login extends Component {
state = {
username: '',
password: ''
};
render() {
return (
<div>
<Consumer>
{(username, password) => (
<div class={style.login}>
<h2>Login Here</h2>
<input onChange={linkState(this, 'username')} type="text" value={username} />
<input onChange={linkState(this, 'password')} type="text" value={password} />
<button onClick={console.log('Clicked')}>Log In </button>
</div>
)}
</Consumer>
</div>
);
}
}
In the main application entry point, the provider wraps the router for all routes within the application so that this information is available (in reality, it will be a token...but testing for now).
app.js
import { h, Component } from 'preact';
import { Router } from 'preact-router';
//import linkState from 'linkstate';
import Header from './components/header';
// Code-splitting is automated for routes
import Login from './routes/login';
import Home from './routes/home';
import Profile from './routes/profile';
import DataProvider from './providers/server';
export default class App extends Component {
/** Gets fired when the route changes.
* @param {Object} event "change" event from [preact-router](http://git.io/preact-router)
* @param {string} event.url The newly routed URL
*/
handleRoute = (e) => {
this.currentUrl = e.url;
};
render() {
return (
<div id="app">
<DataProvider>
<Header />
<Router onChange={this.handleRoute}>
<Home path="/" />
<Login path="/login" />
<Profile path="/profile/" user="me" />
<Profile path="/profile/:user" />
</Router>
</DataProvider>
</div>
);
}
}
When I try to pass down a typescript enum value in a provider the js value is 0. The consumer however receives undefined instead of 0. It works fine when I assign string values to the enum.
Hi. I’m trying to update context from a nested component, similar to this example in the React docs.
However, preact-context
doesn’t behave as expected (and as React.createContext
does).
Here’s an example:
preact-context
: https://codesandbox.io/s/q3n91mr3nw
React.createContext
: https://codesandbox.io/s/nrpx565z50
I am getting this warning in the console:
Consumer used without a Provider
The message is clear enough, but in my case this is not an issue and I would like to suppress this warning.
I'll describe my use case below and show some example code, but the gist of it is: in my case, the use of context is optional and it is expected that it might not be provided. So I would like the option to suppress the warning.
Would you accept a PR?
EDIT: Too late, I already created one :) #25
I am using preact-context
to create a theming solution based on CSS modules. The way it works is that the components will by default render the default class names, but you can load the CSS via CSS Modules and pass the module def to a Provider and the components will pick it up and use the modified class names.
The way I did this was to have a default definition in each component that contains the unmodified class names just like they appear in my CSS.
E.g:
const defaultClasses = { 'test': 'test' }
After loading the CSS through CSS Modules, this will produce a module definition with mangled class names (to provide scoping), that looks like this:
const classes = import('./some/styles.css');
// classes == { 'test': 'test_23rfT4' }
The user of my library can wrap the components in a theme Provider and provide the mangled names so the components will use those names i.s.o the default ones.
The components all use a Consumer defined in a common file and when the user needs a Provider, he gets it from this same file:
theme.jsx
import { h } from 'preact'
import { createContext } from 'preact-context'
export const Theme = createContext({})
export const Provider = Theme.Provider
export const Consumer = Theme.Consumer
export default { Theme, Provider, Consumer }
The components use the Consumer to try to get the property classes
from the context, defaulting it to an empty object. They then merge the classes
over the defaultClasses
and use the resulting definition:
card.jsx
import { h } from 'preact'
import defaultClasses from 'solids/card/classes'
import { createHelper } from '../style-classes'
import { Consumer } from '../theme'
export const Card = (props = {}) => {
const {
// show an outlined card
outlined = false,
children,
// other attributes
...attributes
} = props
return (
<Consumer>{({ classes = {}, scope = 'local' }) => {
classes = { ...defaultClasses, ...classes }
let classNames = createHelper(classes, scope)
attributes.className = classNames(classes.card, {
[attributes.className || attributes.class]: attributes.className || attributes.class,
[classes.outlined]: outlined,
})
return (
<div {...attributes}>
{children}
</div>
);
}}</Consumer>
)
}
export default Card;
This all works like a charm, except that I get this annoying warning, even in the production build.
referring to A Secret parts of React New Context API https://medium.com/@koba04/a-secret-parts-of-react-new-context-api-e9506a4578aa
Does preact-context provide something similar?
Cheers
Hi,
is there a way to change the tag name of Context.Provider? Now it generates <span>
which results oftentimes in invalid markup.
It would also be nice if the props would extends some sort of HTMLAttributes, so one could use that wrapper to add some classes or other valid html attributes to it.
Just to avoid unnecessary div-soup.
Cheers
I'm currently experiencing an issue when multiple consumers are conditionally rendered:
https://jsfiddle.net/jesusvilar/9h28gymb/36/
Notice that in the last consumer, value is not updated.
It seems to me that registration to the provider happens immediately after rendering a consumer. If changing the value of provider is done at the same time as conditionally rendering a consumer, the consumer won't pick the updated value.
Is the emitter not supposed to update the value when it registers?
https://github.com/valotas/preact-context/blob/master/src/context-value-emitter.ts#L20
Also, same jsfiddle using React 16: https://jsfiddle.net/jesusvilar/nLaqb7os/
I use react-bootstrap, which uses react.createContext:
TypeError: _react.default.createContext is not a function
at Object.<anonymous> (/var/home/jgillich/Development/polyfresh/node_modules/react-bootstrap/lib/ThemeProvider.js:17:43)
Is there any way to fix these imports? I tried to play around with webpack resolve alias, but that didn't work. :( I do have an alias for react -> preact-compat though.
I don't see a way to access context value beyond JSX template and how to pass the value to child component via html attribute.
This way I cannot use this value to send it over http for example.
Hi,
I'm using preact with typescript and i'm getting this message whenever i wanna pass object to the provider value "[ts] Type '{ test: string; }' is not assignable to type 'string'.
context.d.ts(4, 5): The expected type comes from property 'value' which is declared here on type 'Readonly<ProviderProps & Attributes & { children?: string | number | object | VNode | ComponentChild[] | null | undefined; ref?: Ref | undefined; }>'
By definition i can only pass a string ?
Thanks
Gil
Below are excerpts from the files in node_modules/preact-context/dist/
after installing from npm. It appears that window.__extends
is being mangled to window.t
by the minification build step, but it probably should be excluded to function properly. As it is, if there is a global variable named t
on the page, it will be used as __extends
and cause preact-context
to break.
uglifyjs
is being passed the option --mangle-props regex=/^_/
, which matches __extends
, so maybe an exception should be added using reserved
?
var __extends = (window && window.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
e =
(window && window.t) ||
((i =
Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array &&
function(n, t) {
n.__proto__ = t;
}) ||
function(n, t) {
for (var i in t) t.hasOwnProperty(i) && (n[i] = t[i]);
}),
function(n, t) {
function r() {
this.constructor = n;
}
i(n, t),
(n.prototype = null === t ? Object.create(t) : ((r.prototype = t.prototype), new r()));
});
Hi! This module is great but I have one very minor suggestion. If you change how createContext
is exported, this module can then be used as a drop-in replacement for React components that use create-react-context (in the same way that you can drop-in preact-compat to replace react-dom).
For example, in Webpack you could do something like:
module.exports = {
...
resolve: {
alias: {
"react": "preact-compat",
"react-dom": "preact-compat",
"create-react-context": "preact-context"
}
},
...
}
However, since create-react-context exports on default
and preact-context exports on createContext
, this is not possible without something hacky.
This would be a breaking change so I understand if you'd want to refrain from doing this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.