react-native-root-siblings
Advanced tools
Comparing version 4.1.1 to 5.0.0
@@ -1,4 +0,4 @@ | ||
import { ReactNode } from 'react'; | ||
import React, { ReactNode } from 'react'; | ||
export default function ChildrenWrapper(props: { | ||
children?: ReactNode; | ||
}): JSX.Element; | ||
}): React.JSX.Element; |
@@ -1,2 +0,2 @@ | ||
import { Component, ReactChild, ReactNode } from 'react'; | ||
import React, { Component, ReactNode } from 'react'; | ||
import RootController from './RootController'; | ||
@@ -6,3 +6,3 @@ interface RootSiblingsProps { | ||
renderSibling?: (sibling: ReactNode) => ReactNode; | ||
children: ReactChild; | ||
children?: ReactNode; | ||
} | ||
@@ -16,9 +16,8 @@ interface RootSiblingsState { | ||
export default class extends Component<RootSiblingsProps, RootSiblingsState> { | ||
private updatedSiblings; | ||
private siblingsPool; | ||
constructor(props: RootSiblingsProps); | ||
componentDidMount(): void; | ||
componentDidUpdate(): void; | ||
componentWillUnmount(): void; | ||
private siblingsPool; | ||
private updatedSiblings; | ||
private unmounted; | ||
render(): React.JSX.Element; | ||
private commitChange; | ||
@@ -28,4 +27,3 @@ private invokeCallback; | ||
private wrapSibling; | ||
render(): JSX.Element; | ||
} | ||
export {}; |
@@ -7,5 +7,4 @@ import React, { Component } from 'react'; | ||
super(props); | ||
this.updatedSiblings = new Set(); | ||
this.siblingsPool = []; | ||
this.updatedSiblings = new Set(); | ||
this.unmounted = false; | ||
this.state = { | ||
@@ -17,3 +16,3 @@ siblings: [] | ||
this.props.controller.setCallback((id, change) => { | ||
setImmediate(() => this.commitChange(id, change)); | ||
setTimeout(() => this.commitChange(id, change)); | ||
}); | ||
@@ -24,9 +23,9 @@ } | ||
} | ||
componentWillUnmount() { | ||
this.unmounted = true; | ||
render() { | ||
return (<> | ||
{this.props.children} | ||
{this.renderSiblings()} | ||
</>); | ||
} | ||
commitChange(id, { change, element, updateCallback }) { | ||
if (this.unmounted) { | ||
return; | ||
} | ||
const siblings = Array.from(this.siblingsPool); | ||
@@ -92,9 +91,3 @@ const index = siblings.findIndex(sibling => sibling.id === id); | ||
} | ||
render() { | ||
return (<> | ||
{this.props.children} | ||
{this.renderSiblings()} | ||
</>); | ||
} | ||
} | ||
//# sourceMappingURL=RootSiblings.js.map |
@@ -1,2 +0,2 @@ | ||
import { ReactNode } from 'react'; | ||
import React, { ReactNode } from 'react'; | ||
export declare function setSiblingWrapper(wrapper: (sibling: ReactNode) => ReactNode): void; | ||
@@ -13,5 +13,5 @@ export default class RootSiblingsManager { | ||
inactive?: boolean; | ||
}): JSX.Element; | ||
}): React.JSX.Element; | ||
export declare function RootSiblingPortal(props: { | ||
children: ReactNode; | ||
}): null; |
import React, { useEffect, useState } from 'react'; | ||
import { AppRegistry } from 'react-native'; | ||
import ChildrenWrapper from './ChildrenWrapper'; | ||
@@ -9,12 +8,6 @@ import wrapRootComponent from './wrapRootComponent'; | ||
} | ||
if (!global.__rootSiblingsInjected && !global.__rootSiblingsDisabled) { | ||
AppRegistry.setWrapperComponentProvider(() => { | ||
return Root; | ||
}); | ||
global.__rootSiblingsInjected = true; | ||
} | ||
export function setSiblingWrapper(wrapper) { | ||
siblingWrapper = wrapper; | ||
} | ||
const { Root, manager: defaultManager } = wrapRootComponent(ChildrenWrapper, renderSibling); | ||
const { manager: defaultManager } = wrapRootComponent(ChildrenWrapper, renderSibling); | ||
let uuid = 0; | ||
@@ -21,0 +14,0 @@ const managerStack = [defaultManager]; |
@@ -1,9 +0,10 @@ | ||
import { Component } from 'react'; | ||
import { Component, ReactNode } from 'react'; | ||
interface Props { | ||
shouldUpdate: boolean; | ||
children: ReactNode; | ||
} | ||
export default class extends Component<Props> { | ||
shouldComponentUpdate(nextProps: Props): boolean; | ||
render(): {} | null | undefined; | ||
render(): string | number | true | import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined; | ||
} | ||
export {}; |
@@ -1,2 +0,2 @@ | ||
import { ComponentType, FunctionComponent, ReactNode } from 'react'; | ||
import { ComponentType, PropsWithChildren, ReactNode } from 'react'; | ||
export interface RootSiblingManager { | ||
@@ -6,5 +6,5 @@ update(id: string, element: ReactNode, callback?: () => void): void; | ||
} | ||
export default function wrapRootComponent<T = {}>(Root: ComponentType<T>, renderSibling?: (sibling: ReactNode) => ReactNode): { | ||
Root: FunctionComponent<T>; | ||
export default function wrapRootComponent<T extends PropsWithChildren>(Root: ComponentType<T>, renderSibling?: (sibling: ReactNode) => ReactNode): { | ||
Root: ComponentType<T>; | ||
manager: RootSiblingManager; | ||
}; |
{ | ||
"version": "4.1.1", | ||
"version": "5.0.0", | ||
"name": "react-native-root-siblings", | ||
@@ -8,2 +8,5 @@ "repository": { | ||
}, | ||
"files": [ | ||
"lib" | ||
], | ||
"license": "MIT", | ||
@@ -25,18 +28,16 @@ "main": "./lib/RootSiblingsManager.js", | ||
"devDependencies": { | ||
"@types/react": "^17.0", | ||
"@types/react-native": "^0.63", | ||
"@typescript-eslint/eslint-plugin": "^2.6.1", | ||
"@typescript-eslint/parser": "^2.6.1", | ||
"eslint": "^6.7.2", | ||
"eslint-config-prettier": "^6.7.0", | ||
"eslint-plugin-import": "^2.19.1", | ||
"eslint-plugin-prettier": "^3.1.1", | ||
"eslint-plugin-react": "^7.17.0", | ||
"eslint-plugin-react-hooks": "^2.3.0", | ||
"eslint-plugin-react-native": "^3.8.1", | ||
"husky": "^3.1.0", | ||
"lint-staged": "^9.5.0", | ||
"np": "^5.2.1", | ||
"prettier": "^1.19.1", | ||
"typescript": "^3.7.3" | ||
"@types/react": "^18.2", | ||
"@typescript-eslint/eslint-plugin": "^6.7.4", | ||
"@typescript-eslint/parser": "^6.7.4", | ||
"eslint": "^8.50.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"eslint-plugin-import": "^2.28.1", | ||
"eslint-plugin-prettier": "^5.0.0", | ||
"eslint-plugin-react": "^7.33.2", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-native": "^4.1.0", | ||
"husky": "^8.0.3", | ||
"lint-staged": "^14.0.1", | ||
"prettier": "^3.0.3", | ||
"typescript": "^5.2.2" | ||
}, | ||
@@ -43,0 +44,0 @@ "keywords": [ |
120
README.md
## react-native-root-siblings [![npm version](https://badge.fury.io/js/react-native-root-siblings.svg)](http://badge.fury.io/js/react-native-root-siblings) | ||
--- | ||
Version 4.x requires react-native version >= 0.59, 3.x requires react-native version >= 0.47 | ||
The easiest way to create overlays(`Modal`, `Popover`, `Dialog` etc) for both `react` and `react-native`. | ||
Add sibling elements after your app root element. | ||
The created sibling elements are above the rest of your app elements. | ||
This can be used to create a `Modal` component or something should be over your app. | ||
Make your own `showModal` and use it in any component without any `isShow` state or even in a pure function call. | ||
# BREAKING CHANGE | ||
```jsx | ||
import { ReactNode } from 'react'; | ||
import RootSiblingsManager from 'react-native-root-siblings'; | ||
## For react native >= 0.62 | ||
export const showModal = (renderModal) => { | ||
let rootNode; | ||
const onClose = () => { | ||
rootNode?.destroy(); | ||
rootNode = null; | ||
}; | ||
rootNode = new RootSiblingsManager(renderModal(onClose)); | ||
return onClose; | ||
}; | ||
The [new LogBox component](https://github.com/facebook/react-native/blob/0b9ea60b4fee8cacc36e7160e31b91fc114dbc0d/Libraries/ReactNative/AppRegistry.js#L298-L309) would impact this component's initialization. To make it work we have to explicitly insert a mount point in your app like this: | ||
import WelcomeModal from './WelcomeModal'; | ||
export function showWelcomeModal() { | ||
showModal((onClose) => <WelcomeModal onClose={onClose} />); | ||
} | ||
// ... | ||
function HomeScreen() { | ||
return <Button onClick={showWelcomeModal}>Welcome!</Button> | ||
} | ||
setTimeout(showWelcomeModal, 3000); | ||
``` | ||
// in your entry file like `App.js` | ||
import { RootSiblingParent } from 'react-native-root-siblings'; | ||
// in your render function | ||
return ( | ||
<RootSiblingParent> // <- use RootSiblingParent to wrap your root component | ||
<App /> | ||
</RootSiblingParent> | ||
); | ||
--- | ||
## Installation | ||
```sh | ||
npm i react-native-root-siblings | ||
``` | ||
You can skip this step if your react-native is lower than 0.62. And actually you can inject RootSiblingParent into anywhere like a react portal, for example if you have multiple rootviews you can choose which one to hold the root siblings. | ||
Insert `RootSiblingParent` between your providers and root app in your root render function. | ||
## 4.x | ||
From 4.0 the redux store context injection is not enabled by default, the redux store context should be set by a context wrapper. | ||
```jsx | ||
import { RootSiblingParent } from 'react-native-root-siblings'; | ||
return ( | ||
<SomeProviders> | ||
<RootSiblingParent> // <- use RootSiblingParent to wrap your root component | ||
<App /> | ||
</RootSiblingParent> | ||
</SomeProviders> | ||
); | ||
``` | ||
import { setSiblingWrapper } from 'react-native-root-siblings'; | ||
import { Provider } from 'react-redux'; | ||
// const store = ... get store; | ||
`RootSiblingParent` works as a mounting base and can be mounted multiple times. Only the last mounted one would be active. | ||
// Call this before using redux context inside RootSiblings. | ||
setSiblingWrapper((sibling) => <Provider store={store}>{sibling}</Provider>); | ||
In react native, a view has a higher hierarchy if it's more close to the root level. | ||
```jsx | ||
<RootSiblingParent> | ||
<RootView> // <- the highest view | ||
<NavigationView> | ||
<ScreenView> // <- the lowest view | ||
{ /* what if you want to show a fullscreen modal here? | ||
* usually you have to use a Native Modal which is even higher than RootView | ||
* but it's buggy and has a lot of limitations | ||
*/} | ||
<RootSiblingPortal> | ||
{ /* View put in here would be transported to RootSiblingParent | ||
* So it can have a same hierarchy as the RootView to cover any other views | ||
*/} | ||
<View> | ||
</View> | ||
</RootSiblingPortal> | ||
</ScreenView> | ||
</NavigationView> | ||
</View> | ||
</RootSiblingParent> | ||
``` | ||
You can also use `setSiblingWrapper` to provide other context into each sibling node. | ||
In react we have `createPortal` but still it's not so convenient as it can not be used outside of a component. | ||
`react-native-root-siblings` provides the most possible flexibility: | ||
## 3.x | ||
From 3.0 the default style has been removed from the element. | ||
https://github.com/magicismight/react-native-root-siblings/commit/75b1f65502f41a5ecad0d17fd8d6ebb400365928 | ||
### Add it to your project | ||
Run `npm install react-native-root-siblings --save` | ||
## Usage | ||
### USAGE | ||
This library can add element above the root app component registered by `AppRegistry.registerComponent`. | ||
### Imperative API | ||
#### Class API | ||
1. Create sibling element | ||
```js | ||
```jsx | ||
let sibling = new RootSiblings(<View | ||
@@ -70,7 +104,7 @@ style={{top: 0,right: 0,bottom: 0,left: 0,backgroundColor: 'red'}} | ||
and returns a sibling instance. | ||
You can create a sibling anywhere inside your react native code. | ||
You can create a sibling anywhere, no matter in a component, hook or even a pure function. | ||
2. Update sibling element | ||
```js | ||
```jsx | ||
sibling.update(<View | ||
@@ -92,5 +126,5 @@ style={{top: 10,right: 10,bottom: 10,left: 10,backgroundColor: 'blue'}} | ||
#### Component API | ||
### Component API | ||
``` | ||
```jsx | ||
import { RootSiblingPortal } from 'react-native-root-siblings'; | ||
@@ -111,5 +145,5 @@ | ||
### EXAMPLE | ||
## EXAMPLE | ||
```js | ||
```jsx | ||
import React, { | ||
@@ -214,3 +248,3 @@ AppRegistry, | ||
### RUN EXAMPLE | ||
## RUN EXAMPLE | ||
@@ -217,0 +251,0 @@ 1. fork this repository |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14
250
28286
21
347
1