mridgway / hoist-non-react-statics Goto Github PK
View Code? Open in Web Editor NEWCopies non-react specific statics from a child component to a parent component
License: Other
Copies non-react specific statics from a child component to a parent component
License: Other
Using typescript the third argument is called 'customStatic'. Which seems to indicate you can provide more statics, rather than provide a list of statics to avoid. I think the name can be more clear.
Please release a new version of the library which contains 604a391.
Also, please consider renaming your primary branch name to "main" instead of "master".
There has been a lot of churn in the type definitions which I am unable to test since I do not use TypeScript. Since the guidance from the TypeScript team is to use DefinitelyTyped for modules that are not natively written in TypeScript, that is what should be done.
Once a type definition has been published to DefinitelyTyped, I will remove the embedded index.d.ts file.
React-is type check methods like isFragment
isMemo
are only applicable for element types as the readme says, not components.
This package allows you to test arbitrary values and see if they're a particular React element type.
This LOC will always return false here:
https://github.com/mridgway/hoist-non-react-statics/blob/master/src/index.js#L54
The React-is
first checks if the passed param is at least an element else it doesn't check its type at all.
Hello,
I'm using some libraries that depend on hoist-non-react-statics
and from what I can tell, the latest 3.3.0 version on github does not have the dist/
folder, so the file specified in package.json # main
cannot be resolved.
The link I used to download was:
https://github.com/mridgway/hoist-non-react-statics/releases/tag/v3.3.0
Additionally, my yarn
installation pointed to:
I'm not sure if this is an issue with my personal yarn
installation, issues with libraries that are depending on an incorrect version of this package, or if this library needs to have npm run build
run before creating the release - I'm still in "investigative" mode, but thought I'd put this up to see if others are having the same issue or if you knew how to resolve.
Thanks!
See styled-components/styled-components#1972
We use the library in styled-components like this https://github.com/styled-components/styled-components/blob/9d72abe32754ae9dd0bf9edf8a652ea592bdd06a/src/models/StyledComponent.js#L351-L362
Not really sure what to make of this 😕
I use
function(WrappedComponent) {
class Component extends PureComponent {
....
}
return hoistNonReactStatics(Component, WrappedComponent);
}
I didn't understand what this library did at first, and I think it's a bit tricky for people who are new to React. Would it be worth adding a more detailed explanation of what the library does to the README?
If others think this would be valuable, I can submit a PR with such an explanation.
React 16.3+ forwardRef() will return a new component (type is object
) that contains a render
function and a $$typeof
Symbol, and these should not be copied, otherwise may cause some bug
import React, { Component, createRef, forwardRef } from 'react';
import hoistStatics from 'hoist-non-react-statics';
function hoist(WrappedComponent) {
return function createHoistedComponent(TargetComponent) {
const HoistedComponent = forwardRef((props, ref) => (
<TargetComponent {...props} forwardedRef={ref} />
));
if (WrappedComponent) {
HoistedComponent.displayName = `hoisted(${WrappedComponent.displayName})`;
hoistStatics(HoistedComponent, WrappedComponent /* , { render: true } */);
}
return HoistedComponent;
};
}
function extraPropsHoc(extra) {
return (WrappedComponent) => {
@hoist(WrappedComponent)
class Bar extends Component {
render() {
const { forwardedRef, ...props } = this.props;
return <WrappedComponent {...props} {...extra} ref={forwardedRef} />;
}
}
return Bar;
};
}
@extraPropsHoc({ foo: 'foo' })
@extraPropsHoc({ bar: 'bar' })
class Foo extends Component {
static displayName = 'Foo';
log() {
console.log('this.props', this.props);
}
render() {
return <h1>Hello</h1>;
}
}
export default class App extends Component {
ref = createRef();
componentDidMount() {
this.ref.current.log();
}
render() {
return <Foo ref={this.ref} />;
}
}
this.props {bar: "bar"}
this.props {foo: "foo", bar: "bar"}
If I uncomment hoistStatics(HoistedComponent, WrappedComponent /* , { render: true } */)
, it will work as expected.
I'm using HOC to annotate components with custom metadata. To avoid name collisions I was planning to use ES2015 Symbols. But this approach fails if component is later wrapped with for example react-redux connect.
Since this library is de facto standard for hoisting statics I thought it would be nice to make it copy symbol properties as Object.assign
does.
Made up a PR #17
Since the latest version I get the misleading warning from PropTypes despite the prop is being provided:
Warning: Failed prop type: The prop
xxx
is marked as required inFoo
, but its value isundefined
.
Compare the same code with different versions in CodeSandbox and check the console:
Locally I tried to revert this line:
hoist-non-react-statics/src/index.js
Line 41 in 69090b6
Back to this:
hoist-non-react-statics/src/index.js
Line 32 in 03a7f2f
And it seemed to work again. I am not sure what's happening there and I did not investegate further, just wanted to share some insights.
It seems that there's some unpushed changes somewhere... There's a version 1.0.5 of this package released on npm:
$ npm info hoist-non-react-statics
{ name: 'hoist-non-react-statics',
description: 'Copies non-react specific statics from a child component to a parent component',
'dist-tags': { latest: '1.0.5' },
versions:
[ '1.0.0',
'1.0.1',
'1.0.2',
'1.0.3',
'1.0.4',
'1.0.5' ],
maintainers: 'mridgway <[email protected]>',
time:
{ modified: '2016-02-02T21:13:42.645Z',
created: '2015-05-27T23:39:34.473Z',
'1.0.0': '2015-05-27T23:39:34.473Z',
'1.0.1': '2015-05-30T23:31:11.202Z',
'1.0.2': '2015-06-08T23:39:14.515Z',
'1.0.3': '2015-08-10T20:39:03.169Z',
'1.0.4': '2016-02-02T19:13:04.294Z',
'1.0.5': '2016-02-02T21:13:42.645Z' },
/* ... */ }
But package.json on HEAD says the latest version is 1.0.4. :-)
The latest release doesn't include the type definition file, breaking current libraries.
The AMD definition does not declare the dependency on react-is and uses the global require, not the AMD local require.
Two possible ways of handling this would be a correct AMD definition here would be either
define(["react-is"], function(ReactIs) {...});
or
define(["require", "react-is"], function(require) { var ReactIs = require("react-is"); ...});
Hi~~
I have a react project and I want to upgrade react-redux from v5.0.7 to v5.1.1, but when I restart my project, I get some error from hoist-non-react-statics
as follows:
hoist-non-react-statics
is a dependency of react-redux and I notice that hoist-non-react-statics has changed package.json main field to a cjs file from v2.5.1. The require syntax isn't supported in browser.
Is it a bug for hoist-non-react-statics ???
Currently the semver matches react v16 only, which introduces duplicated react-is
(v16 and v17).
class A extends React.Component {
static test3 = 'A';
}
class B extends A {
static test2 = 'B';
}
class C {
static test1 = 'C';
}
const D = hoistNonReactStatic(C, B);
console.log('D.test1', D.test1); // 'C'
console.log('D.test2', D.test2); // 'B'
console.log('D.test3', D.test3); // undefined
This is caused by Object.getOwnPropertyNames
and Object.getOwnPropertySymbols
not deeply resolving inherited properties defined on the prototype chain. I have created a utility to get around this which you can use as a dependency or copy the basic logic object-props
.
I'm using script type="module" to load two react components into the browser.
Parent provides a context.
Child consumes a context.
As long as both components come from the same bundle the Child gets the correct context.
However if I load the components from their own bundles. The context is lost.
My workaround for this is to pass in the context as props.
But then the Child cannot be a context Consumer, because it will overwrite the props.
So my question is, if some property exists both as a prop and in the context, which should win?
If the property is undefined in the context it would be nice to keep the prop.
(If anyone has other suggestions on how to make the correct context available to the child, please let me know.)
I'm opening a separate issue for discoverability starting with my original comment #55 (comment)
@mridgway Why is the peer to react >= 14 required? As far as I understood
react-is
it would just not recognize components from before v14. Since you currently only usereact-is
to detect types that were added in16.3.0
this shouldn't be an issue.
In addition I Did some more research and the original PR did not mention any version requirement for react. The RFC and the original commit explicitly say that it has no requirement to react.
This is currently causing trouble with every package that wants to support older and new react versions. Getting all affected to release a new major and drop support for react below 14 just to bump hoist-non-react-statics
will take time and wouldn't be needed if we could simply backport v3 to a new minor in v2.
Edit:
So I guess you're refering to facebook/react#12882 (comment)
Which previous versions of react does
react-is
support?Should support versions 0.14+
It relies on
$$typeof
(added in 0.14), so it will not support <= 0.13
But again since forwardRef
was only added in 16.3 it wouldn't matter in that case. It's only dangerous if you plan on using isValidElementType
which would return false for components in react <= 0.13
Use the latest react-is feature "isMemo" but without upgrading to the correct version of react-is
WHY?
There are third argument: 'blacklist'. It's very useful for my situation.
Would you mention it in docs? And I will be sure that it can be used safely.
I'm a little bit unsure why this is needed:
var defineProperty = Object.defineProperty;
var getOwnPropertyNames = Object.getOwnPropertyNames;
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
var getPrototypeOf = Object.getPrototypeOf;
var objectPrototype = getPrototypeOf && getPrototypeOf(Object);
Are those property lookups considered costful so they are cached? By inlining them we could save 34 bytes (checked on minified and gzipped bundle)
I see that typescript types consider that the source component and the target should share the same props. But I often use this module with components that have completely different props (because several HoC enhance the source component).
Shouldn't the type declaration allow for source and target to have no props in common ? like so:
function hoistNonReactStatics<Own, Custom>(
TargetComponent: React.ComponentType<Own>,
SourceComponent: React.ComponentType<Custom>,
customStatic?: any): React.ComponentType<Own>;
are you aware of any incompatibilities with React 15.x? (readme mentions 0.13-0.14 as supported version)
If i use hoist-non-react-statics In some low version browsers,like oppo r9m android5.1 ,there will be an error in console。
The error information is “Object.getPrototypeOf called on non-object” 。
And there is the reason。
Is it better to add some fault-tolerant logic?
propTypes
and defaultProps
are still processed statics for react in forwardRef
components (other statics might be too but those two were the most straight forward to verify). They should therefore not be hoisted.
Edit:
displayName might be another issue. It's probably safe to assume that FORWARD_RED_STATICS
should be merged if the sourceComponent is a forwardRef component instead of replacing all react statics.
Edit: broken example; see #66 (comment) for fixed code
import React from "react";
import ReactDOM from "react-dom";
import hoistStatics from "hoist-non-react-statics";
import PropTypes from 'prop-types';
const App = React.forwardRef((props, ref) => <div {...props} ref={ref} />)
App.propTypes = {
id: PropTypes.number.isRequired
}
App.defaultProps = {
children: 'Hello, World',
}
const withDefaultId = id => Component => {
function EnhancedComponent({innerRef, ...props}) {
return <Component id={id} {...props} ref={innerRef} />
}
return hoistStatics(EnhancedComponent, Component);
}
// we should now be able to savely use AppWithId without propTypes warning
// that `id` is missing since with injected it. But propTypes is being hoisted
// so react warns anyway
const AppWithId = withDefaultId('default-id')(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<React.StrictMode><AppWithId innerRef={r => console.log(r)} /></React.StrictMode>, rootElement);
Looking at styled-components/styled-components#2021, it seems that dropping React <16.0.0 support can reduce the bundle size.
"Copies non-react specific statics from a child component to a parent component."
...
hoistNonReactStatics(targetComponent, sourceComponent);
What is a child and what is a parent (targetComponent or sourceComponent)?
Looks like both 2.5.1 and 2.5.2 versions break react-apollo.
at IncomingMessage.g (events.js:292:16) TypeError: hoistNonReactStatics$1 is not a function
at eval (webpack:///./node_modules/react-apollo/react-apollo.browser.umd.js?:962:20)
at eval (webpack:///./node_modules/react-apollo/node_modules/lodash/_createFlow.js?:68:42)
It works properly with 2.5.0
I have a HOC like this
const asyncComponent = (importComponent) => {
class WrapComp extends React.Component{
state = {
component: null
}
componentDidMount(){
importComponent().then(cmp => {
this.setState({component: cmp.default});
});
}
render(){
const {forwardedRef, ...rest} = this.props
C = this.state.component;
return C ? <C ref={forwardedRef} {...rest} /> : null;
}
}
return React.forwardRef((props, ref) => {
return <WrapComp {...props} forwardedRef={ref} />;
});
}
const component = asyncComponent(() => import('./another.js'))
as you can see, because the importComponent is dynamic imported, I cant use this package before the HOC return
i'm on nextjs, this package doesn't hoist getInitialProps, which is a non react static method.
Hi,
While auditing dependencies, I encountered a minified build in this library's npm package. This unfortunately makes it very difficult to audit the package, and I'd like to suggest removing it and/or distributing it outside of npm instead.
I've written an extensive explanation of the problem and the reasons for my suggestion here, so that I don't end up redoing the same explanation in every affected package: https://gist.github.com/joepie91/04cc8329df231ea3e262dffe3d41f848
Thanks!
hi , Do you have any advice to solve this problem, friends?
`
error: Error: While trying to resolve module hoist-non-react-statics
from file D:\xxx\xx\node_modules\react-redux\lib\components\connectAdvanced.js
, the package D:\xx\xx\node_modules\hoist-non-react-statics\package.json
was successfully found. However, this package itself specifies a main
module field that could not be resolved (D:\xx\xx\node_modules\hoist-non-react-statics\dist\hoist-non-react-statics.cjs.js
. Indeed, none of these files exist:
`
package.json:
{
"name": "xx",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"@react-native-community/art": "^1.2.0",
"@react-native-community/async-storage": "^1.12.1",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-community/netinfo": "^7.1.7",
"@react-native-picker/picker": "^2.2.1",
"@react-navigation/native": "5.x",
"axios": "^0.26.0",
"jalali-moment": "^3.3.10",
"json-beautify": "^1.1.1",
"native-base": "^3.3.4",
"react": "17.0.2",
"react-native": "0.67.3",
"react-native-dialog-input": "^1.0.8",
"react-native-exit-app": "^1.1.0",
"react-native-gesture-handler": "^2.2.0",
"react-native-image-crop-picker": "^0.37.2",
"react-native-image-slider-banner": "^1.0.3",
"react-native-indicator": "^1.2.2",
"react-native-modal": "^13.0.0",
"react-native-safe-area-context": "^3.1.9",
"react-native-screens": "^3.10.2",
"react-native-slideshow": "^1.0.1",
"react-native-svg": "^12.1.0",
"react-native-svg-transformer": "^1.0.0",
"react-native-textinput-effects": "^0.6.2",
"react-native-toast-message": "^2.1.1",
"react-native-vector-icons": "^9.0.0",
"react-native-webview": "^11.17.0",
"react-navigation": "^4.4.4",
"react-navigation-stack": "^2.10.4",
"react-redux": "^7.2.2",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"reselect": "^4.1.5",
"rn-modal-picker": "^0.3.2"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/runtime": "^7.17.2",
"@react-native-community/eslint-config": "^3.0.1",
"babel-jest": "^27.5.1",
"eslint": "^8.11.0",
"jest": "^27.5.1",
"metro-react-native-babel-preset": "^0.69.0",
"react-test-renderer": "17.0.2"
},
"jest": {
"preset": "react-native"
}
}
Is there any interesting in preventing this function from overwriting the statics of the target (either by default or by change of the function's API)?
Hi,
The original reason for having started to hoist non-enumerable properties was babel transpiling static class properties as non-enumerable. Babel no longer does that. Can this be changed back to hoist only enumerables, or can an option be added to hoist non-enumerables or not?
I'm willing to submit a PR changing this if it's welcome
(original change: #3)
Thanks,
Nuno
Hi all.
Could you tell me which version I should use for react-redux typescript.
There is import and using it as inner property.
But in current version inner is main object.
import hoistNonReactStatics = require('hoist-non-react-statics');
hoistNonReactStatics.NonReactStatics
Error
./node_modules/@types/react-redux/index.d.ts
Namespace '"C:/work/School6/test/brillder/node_modules/hoist-non-react-statics/index"' has no exported member 'NonReactStatics'
Please
Hello! I saw that you race for minified size, and I have tried several options to minify the bunde for modern browsers for about 15%:
let x = true
and set all settings from contextTypes: true
to contextTypes: x
- saves ~20b of minified codegetStatics
method to arrow function - 16b of minified codeUnminified souce is also reduced by 400 bytes
Should I provide a PR for all of this or some points are useless?)
Having publishConfig with hardcoded global registry makes impossible to upload this package to custom local offline mirrors (usually used by companies):
"publishConfig":` {
"registry": "https://registry.npmjs.org/"
}
Removing it should not affect regular version release since registry.npmjs.org is default in npm.
Hi
I'm currently experiencing an issue passing a HOC directly to another component that is expecting the React Component to have .prototype.isReactComponent
available. The HOC is react-intl and it's using this package to clone a WrappedComponent. The prototype property is added by React to allow others to easily check if a function is a React Component, which is used by react-table.
Seeing as this package is cloning React components, should this one prototype property be cloned too?
This current fix is to add this manaully before exporting:
const injected = injectIntl(MyComponent);
injected.prototype.isReactComponent = {};
export default injected
please provide an umd format not just commonjs, for using without tools like webpack
i did function:
export const withAppContext = (Component:any):any => {
let r = (props:any):any => {
return <AppContext.Consumer>
{(context: any) => {
return <Component {...props} context={context}/>
}}
</AppContext.Consumer>
};
if (Component.navigationOptions) {
Object.defineProperty(r,'navigationOptions',Component.navigationOptions)
//(r as any)['navigationOptions']= Component['navigationOptions'];
}
//manually just for test, but also does not work
Object.defineProperty(r,'navigationOptions', {
title: 'Home',
})
return r;
};
why this method do not provide a static method 'navigationOptions' when i do render
withAppContext (ReactComponent)
?
I am getting this error:
Uncaught TypeError: Cannot assign to read only property '__reactPatchProxy' of function 'function WrappedComponent() {
_classCallCheck(this, WrappedComponent);
return _possibl...<omitted>... }'hoistNonReactStatics @
:cc @gajus
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.