Passing State in React-Navigation from TabsNavigation to Child StackNavigation - react-native

UPDATE: Now with a Snack Demo
I've created a demo on snack so you can see the issue first hand and help me demonstrate a solution in actual code.
Steps to duplicate
launch app
Tap "GO TO EVENTTABS" button
Tap each tab, noticing that the eventId is in scope for the first three tabs
Tap "More" tab
Tap "TEAM MEMBERS", noticing that eventId is no longer in scope. This is where the problem lies. How do I pass along eventId?
_____________________________
My App has the following navigation hierarchy, where every instance of <> is just a regular component
App <StackNavigator> {
EventList <>
EventTabs <BottomTabNavigator> {
Quests <>
Leaderboard <>
Gallery <>
More <StackNavigator> {
MoreList <>
TeamMembers <>
}
}
}
Upon entering the app, the user's first screen is EventList. They click a button to navigate into EventTabs, so I'm able to use the navigation.navigate() to transition while passing state like so...
EventList.navigation.navigate(EventTabs, passedParams);
To this point, everything makes sense. But TeamMembers also needs access to the passedParams. I'm confused how to pass those along. Hence my question...how do access passedParams from the TeamMembers component? They seem to be scoped just to the EventTabs.
If the answer is to use navigate.setParams(), then I'm not sure where I'd do that.
If the answer is to use NavigationActions.setParams(), then I'm also not sure where I'd do that.

Unfortunately we don't have good support for this, but you could use a function like this to recursively walk your navigation parents in search of the correct param.
function getParam(navigation, paramName) {
const { getParam, dangerouslyGetParent } = navigation;
let parent = dangerouslyGetParent();
let val = getParam(paramName);
while (val === undefined && parent && parent.getParam) {
val = parent.getParam(paramName);
parent = parent.dangerouslyGetParent();
}
return val;
}

The problem seems to continue for version 5.x of react navigation.
For me this works.
function getParentParam(navigation, paramName) {
const { dangerouslyGetParent } = navigation;
let paramValue = null;
const parent = dangerouslyGetParent();
const routes = parent.dangerouslyGetState().routes;
routes.some((r) => {
paramValue = r.params ? r.params[paramName] : null;
return paramValue !== null;
});
return(paramValue);
}
You can iterate over parent.dangerouslyGetParent() according to the depth level you have

Related

React Native language change doesn't affect immediately

I'm using react-native-i18n in my project. After changing language to Arabic all text data are automatically aligned to the Right side of the screen properly. But all other components like Icons are aligning only after re-opening the app.
export function langSet(type){
if(type == "en"){
I18n.locale = type;
const currentLocale = I18n.currentLocale();
}else{
I18n.locale = type;
const currentLocale = I18n.currentLocale();
ReactNative.I18nManager.allowRTL(true);
ReactNative.I18nManager.forceRTL(true);
}
}
the language occurs only if u refresh your DOM, for this you should have to make a change state that will make the DOM to refresh,
make a dumy state
state = {
languageChange : false
}
then after shifting language just below that run the state change as
this.setState({ languageChange: !this.state.languageChange ) }

iview drawer(transfer=false, inner=true ) show in tag outside rather than inside in IE10

Situation is OK in Chrome but also the IE11
With "transfer"(false) and "inner"(true) set, Drawer work as follow link:
https://run.iviewui.com/prdkRwyB
normally effect
Problem occur when using IE10
The drawer show in tag outside rather than inside.
abnormally effect
And The html code of drawer has been place out of its parent tag
If you use F12 developer tools to check the HTML and CSS, you can see that the drawer is outside the iview card body, it seems that this issue is related to iView, you could contact them and feedback this issue.
The screenshot in IE 11:
The screenshot in IE 10:
I found the problem solution.(iview 3.2.2)
iview/src/directives/tansfer-dom.js
This js file handle the DOM transfer job, which lead to drawer panel transfer out of the parent DOM.
inserted (el, { value }, vnode) {
if ( el.dataset && el.dataset.transfer !== 'true') return false;
el.className = el.className ? el.className + ' v-transfer-dom' : 'v-transfer-dom';
const parentNode = el.parentNode;
if (!parentNode) return;
const home = document.createComment('');
let hasMovedOut = false;
if ( value !== false) {
parentNode.replaceChild(home, el); // moving out, el is no longer in the document
getTarget(value).appendChild(el); // moving into new place
hasMovedOut = true
}
if (!el.__transferDomData) {
el.__transferDomData = {
parentNode: parentNode,
home: home,
target: getTarget(value),
hasMovedOut: hasMovedOut
}
}
},
As file show
if ( value !== false)
The judgment on Line 9 is unappropriated.
After replacing code as below and rebuild the iview by running 'npm run dist',
if( value && value !== false )
drawer show well in IE10

Polymer2 Shadow dom select child element

I am working on a polymer2 shadow dom template project need to select children elements from parent elements. I found this article introduces a way to select child shadow dom elements that like this:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
However, when I tried in my polymer2 project, like this:
//First: works!!
document.querySelector('container')
.shadowRoot.querySelector('app-grid')
.shadowRoot.querySelector('#apps');
//Second: Doesn't work!// got null
document.querySelector('container::shadow app-grid::shadow #apps')
// Thrird: document.querySelector('* /deep/ #apps') // Doesn't work, got null
I really need the second way or the third, which to put selectors in (), but both couldn't work. Does anyone know why the second one doesn't work? Thank you so much!
::shadow and /deep/ has never(?) worked in Firefox, and is depraved in Chrome 63 and later.
Source
Eric Biedelman has written a nice querySelector method for finding all custom elements on a page using shadow DOM. I wouldn't use it myself, but I have implemented it so I can "querySelect" custom elements in the console. Here is his modified code:
// EXAMPLES
// findCustomElement('app-grid') // Returns app-grid element
// findCustomElements('dom-if') // Returns an array of dom-if elements (if there are several ones)
// findCustomElement('app-grid').props // Returns properties of the app-grid element
function findCustomElement(customElementName) {
const allCustomElements = [];
customElementName = (customElementName) ? customElementName.toLowerCase() : customElementName;
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
el.props = el.__data__ || el.__data || "Doesn't have any properties";
if (customElementName && customElementName === el.tagName.toLowerCase()) {
allCustomElements.push(el);
} else if (!customElementName) {
allCustomElements.push(el);
}
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
if (allCustomElements.length < 2) {
return allCustomElements[0] || customElementName + " not found";
} else if (customElementName) {
allCustomElements.props = "Several elements found of type " + customElementName;
}
return allCustomElements;
}
Remove the if (isCustomElement(el)) { statement, and you can querySelect whatever element and get an array of it if several of them exists. You can change findAllCustomElements to implement a smarter querySelect using the recursive loop on shadowDoom as base. Again, I wouldn't use this myself – and instead pass on variables from parent element(s) to children where the children have observers that activates specific behaviors – but I wanted to give you a general implementation of a fallback if nothing else works.
The problem with your question is that you don't give any specifics about WHY you want to select the children in the first place.

How goBack screen in test with detox

I make automatization react native test with detox, It has the next screen sequence A -> B -> C and i wish to go back to the screen B <- C.
Is there a solution for this?
There's a testId on the back button, so you can do this:
await element(by.id("header-back")).tap();
sometimes
await element(by.id("header-back")).tap();
does not work and
await element(by.traits(['button']))
.atIndex(0)
.tap();
selects another button. In that case, you can try to use swipe right on ios assuming that it is a stack navigator. Use the outer container view
await element(by.id('containerView')).swipe('right', 'fast', 0.1);
the solution was to use traits button as follows:
await element(by.traits(['button'])).atIndex(0).tap();
You could go ahead and create a utility
export const pressBack = async () => {
if (device.getPlatform() === 'android') {
await device.pressBack(); // Android only
} else {
await element(by.traits(['button']))
.atIndex(0)
.tap();
}
};
Android: device.pressBack()
iOS: go back last screen #828
If you are using react-native-navigation you can use:
const backButton = () => {
if (device.getPlatform() === 'ios') {
return element(by.type('_UIBackButtonContainerView'));
} else {
return element(by.label('Navigate Up'));
}
};
...
await backButton().tap();
For iOS in detox#17.3.6 & react-native-navigation#6.10.1 you can use:
return element(by.id('pop'));
Another way that works is
await element(by.id('header-back')).atIndex(0).tap()
This uses the built in testID that the default back button that comes with react-navigation v5. You may need to mess with the atIndex() number since for me it seems to match 2 back buttons but the first one was the one I was looking for.

Is it possible to change transitions in react native navigator?

I have 3 different react native components and I am using the Navigator to navigate between them. In my first view I define the navigator:
View 1
<Navigator
ref="nav"
renderScene={#renderScene}
initialRoute={#renderContent(I18n.t("Incidents"))}
configureScene={ ->
transition = Navigator.SceneConfigs.HorizontalSwipeJump
transition.gestures = null
transition
}
/>
As you can see the transition is HorizontalSwipeJump.
View 2
#props.navigator.push
component: IncidentScreen
incidentId: incident.id
sceneConfig: -> Navigator.SceneConfigs.FloatFromBottomAndroid
As you can see, I am trying move into view #3 using FloatFromBottomAndroid, however, it's not working.
By looking at the source code for RN I can see that the navigator.push method get's the animation from the props:
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route),
]);
So what can I do?
Thanks a lot.
You have to go digging into the react-native source here for the list of SceneConfigs, but here's the current list as of writing:
PushFromRight
FloatFromRight
FloatFromLeft
FloatFromBottom
FloatFromBottomAndroid
FadeAndroid
HorizontalSwipeJump
HorizontalSwipeJumpFromRight
VerticalUpSwipeJump
VerticalDownSwipeJump
Example usage:
<Navigator
configureScene={(route) => {
if (someCondition) {
return Navigator.SceneConfigs.HorizontalSwipeJump;
} else {
return Navigator.SceneConfigs.PushFromRight;
}
}}
/>
Ok, I figure it out. I was missing this part in View 1:
configureScene={ (route) ->
if route.sceneConfig
route.sceneConfig
else
transition = Navigator.SceneConfigs.HorizontalSwipeJump
transition.gestures = null
transition
}
If anyone is still looking at this, you can push without animation by just reseting the routes to what you want them to end up being. This is assuming that you don't do anything special with your routes like save the forward routes or anything.
if( !shouldAnimate )
{
var routeStack = this.refs.mainNav.state.routeStack;
routeStack.push(newRoute);
this.refs.mainNav.immediatelyResetRouteStack(routeStack);
}
else
{
this.refs.mainNav.push(feature);
}
Where mainNav is the ref of my Navigator. Hope this helps.