React navigation Tab in Stack with button in Header - react-navigation

I'd like to have a TabNavigator embedded in a StackNavigator with a button in the Header used to navigate in the main Stack.
Here is a snack of my issue: https://snack.expo.io/#guigui64/tabs-in-stack
The propblem is when I create the TabNavigator:
const TabNavigator = createMaterialTopTabNavigator(
{
ScreenA,
ScreenB,
},
{
navigationOptions: {
headerTitle: 'Title when in tab',
headerRight: (
<Button
onPress={() => this.props.navigation.navigate('C')} // here is the issue !
title="ScreenC"
/>
),
},
}
);
I also tried creating a Component with static navigationOptions and have render() return a TabNavigator. In this case, the header appears fine but not the tabs.
Thanks in advance !

Related

How can I add content into a CKeditor5 by clicking on an outside element

I have a few buttons outside of the Ckeditor on my page, and I want to click on them and add content to the Ckeditor5.
By clicking on the buttons, I make an ajax call to retrieve the content from the server, but I'm having trouble inserting it. This is my code right now:
success: function (data) {
ClassicEditor
.create( document.querySelector( '.ckeditor' ), {
language: 'he',
} )
.then( editor => {
editor.model.change( writer => {
const insertPosition = editor.model.document.selection.getFirstPosition();
writer.insertText(data, insertPosition );
} );
} )
.catch( error => {
console.error( error );
} );
}
But this code duplicates my editor, and I don't know how to get the "editor" object without using ".create". can anyone help me with this?

React Navigation 6 attempting to pass different params to same screen

I currently have the following setup an onboarding stack component:
export default function OnboardingStack(props) {
const { auth, activeUser } = useContext(FirebaseContext);
return (
<Stack.Navigator mode="card" headerMode="none">
<Stack.Screen
name="Login"
component={Login}
option={{
headerTransparent: true
}}
/>
<Stack.Screen name="App" component={AppStack} />
</Stack.Navigator>
);
}
Then I can call a component called MemberList which contains a touchable opacity:
<TouchableOpacity style={styles.touchableRow} onPress={ () => navigateMember(item) }>
the method navigateMember I navigate to "Add Member"
const navigateMember = (item) => {
navigation.navigate("Add Member", {
screen: "Member",
params: {
uid: item,
}
}
);
}
Here item is different each time I click it but when I get into the "Member" screen it retains the first original passed uid. Member component contains:
useEffect(() => {
navigation.addListener('focus', () => {
// console.log(navigation);
console.log('this member route');
console.log(route);
})
navigation.addListener('blur', () => {
console.log('leaving blur');
navigation.setParams({
key: route.key,
params: { uid: 'og' },
})
})
}, [])
Each time the uid remains the same. I've tried to reset it when it blurs but it always retains the same params. Any idea what I'm doing wrong?
The solution was to use push as opposed to navigate.
onPress={() => navigation.push('Member')}
More details found in the documentation

Reset react navigation stack history

Update
Changed to use DrawerNavigator as the root for now, but would still like to know if it's possible to use StackNavigator as root but behavior like tabs, or drawers.
Am using reactnavigation v5.
I have the following setup:
<RootStack.Navigator initialRouteName={'SplashNavigator'}>
<RootStack.Screen name={'SplashNavigator'} component={SplashStackNavigator} options={{headerShown: false}} />
<RootStack.Screen name={'DrawerNavigator'} component={DrawerNavigator} />
</RootStack.Navigator>
From SplashNavigator I do a navigation.navigate('DrawerNavigator', {screen:'Home'})
However, the header has a back button that navigates back to the splash screen.
Is there a way to reset the history so it'll take the DrawerNavigator as the main navigator?
Have tried StackActions.replace in HomeScreen but somehow it doesn't recognise the screen.
const HomeScreen = ({navigation}) => {
useEffect(() => {
navigation.dispatch(state => {
console.log(state);
return StackActions.replace('Home')
});
}, []);
...
ExceptionsManager.js:173 The action 'REPLACE' with payload {"name":"Home"} was not handled by any navigator.
Yet I do see the route in the state
"routeNames": [
"Home",
"Promotions"
],
Have tried CommonActions.reset() too but it didn't change anything.
const HomeScreen = ({navigation}) => {
useEffect(() => {
navigation.dispatch(state => {
console.log(state);
return CommonActions.reset({...state, key:null})
});

Null Screen in React Navigation v5?

In React navigation v4 we pass the null screen like this:
`Screen: {
screen: () => null,
navigationOptions: {
tabBarIcon: <AddButton />
}
},`
But How to I can do it in react navigation 5?
I choose this path for the screens I want to hide. May be help you. I manage isLogin with Mobx.
{MenuStore.isLogin ? <></>
: <Drawer.Screen
name="Login"
component={LoginStackScreen}
/>
You can pass <NavigatorName.Screen name="ScreenName">{() => null}</NavigatorName.Screen> to pass null as a screen.
You can use event listener on the tab bar
<Tab.Screen
name="Plus"
component={Add}
listeners={{
tabPress: (e) => {
e.preventDefault(); // — > the main part
},
}}
/>
Where Add is
const Add = () => { return null; };

Redux: How to pass store to form created outside the Provider scope

I have written code, which uses a Modal dialog to display a form.
My react app is rendered at "root"
index.html
<div id="root"></div>
App.js
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<ExampleBasic/>
</Provider>
, document.getElementById('root'));
ExmpleBasic.js
Please ignore state management in component here. this is just for example.
import React, { PureComponent } from 'react';
import Lorem from 'react-lorem-component';
import Modal from '#atlaskit/modal-dialog';
import Button from '#atlaskit/button';
export default class ExampleBasic extends PureComponent {
state = { isOpen: false }
open = () => this.setState({ isOpen: true })
close = () => this.setState({ isOpen: false })
secondaryAction = ({ target }) => console.log(target.innerText)
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<BasicFormContainer />
</Modal>
)}
</div>
);
}
}
BasicFormContainer.js
const mapStateToProps = state => ({
addDesignation: state.designations.addDesignation,
});
const mapDispatchToProps = dispatch => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicForm);
BasicForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
class BasicForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(values) {
console.log(values);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit)}>
<Field
name="designationName"
component="input"
placeholder="Name"
label="Enter name"
autoFocus
/>
</form>
);
}
}
export default reduxForm({
form: 'BasicForm',
enableReinitialize: true,
})(BasicForm);
However modal is rendered using portal, outside current DOM.
As modal is rendered outside the scope of redux context, it is not getting the
store. and i am getting an error "Uncaught Error: Field must be inside a component decorated with reduxForm()"
Below is link to same kind of problem, where redux form within portal is not working.
Redux Form Wrapped Inside Custom Portal Component?
in React 16 it is handled by portals, but version before then that you can try something like as follow.
export default class ExampleBasic extends PureComponent {
...
static contextTypes = { store: React.PropTypes.object };
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<Provider store={this.context.store}>
<BasicFormContainer />
</Provider>
</Modal>
)}
</div>
);
}
}
You need to pass in the values of BasicForm.js to the Redux store and dispatch an action from there itself and not from the BasicFormContainer.js. This way, the Modal remains inside of the scope of your root element and thus there is no need to access the store outside of the Provider.
Then update the Redux store based on the values entered in the form. Once, the store is updated, you can then access it from anywhere in your application such as Modal in your case.
I downgraded to version 2.1.0 to solve the problem.

Resources