react-native-navigation navigator is undefined custom button - javascript

I am using React Native Navigation by Wix (https://github.com/wix/react-native-navigation)
I am using Redux with my app also.
I am trying to add a custom button to my top bar, so I can trigger opening and closing the drawer.
I am adding a drawer to the tab as follows :
Navigation.startTabBasedApp({
tabs: [
{
label: 'Home',
screen: 'swiftyApp.Home',
icon: icons.homeOutline,
selectedIcon: icons.home,
title: 'Home',
navigatorStyle,
navigatorButtons: {
leftButtons: [
{
id: 'custom-button',
component: 'CustomButton',
passProps: {
text: 'Hi!'
}
}
]
}
}
],
drawer: {
left: {
screen: 'swiftyApp.Drawer',
passProps: {}
},
style: {
drawerShadow: false,
contentOverlayColor: 'rgba(0,0,0,0.25)',
leftDrawerWidth: 75,
rightDrawerWidth: 25
},
type: 'MMDrawer',
animationType: 'slide',
disableOpenGesture: false
},
appStyle: {
orientation: 'portrait',
hideBackButtonTitle: true
}
});
});
My Custom Button component looks like
const CustomButton = props => {
console.log(props);
const { text, navigator } = props;
return (
<TouchableOpacity
style={[styles.button, { backgroundColor: 'tomato' }]}
onPress={() =>
navigator.toggleDrawer({
side: 'left',
animated: true
})
}
>
<View style={styles.button}>
<Text style={{ color: 'white' }}>{text}</Text>
</View>
</TouchableOpacity>
);
};
The button displays and the styles are applied as expected. However when clicking the button an exception is thrown that the onPress fails as navigator.toggleDrawer is undefined, on checking the output of the navigator props being passed in, I can see in the log:
2017-11-25 13:33:48.703 [info][tid:com.facebook.react.JavaScript] '************', { testID: 'CustomButton',
navigator: undefined,
passPropsKey: 'customButtonComponent3',
rootTag: 21,
text: 'Hi!' }
Navigator is indeed undefined. I cannot for the life of my word out why.
How do I pass navigator into something such a custom button for the navbar, so I can toggle a drawer open or trigger a modal?

Custom buttons aren't associated with a navigator. You'll need to set the button in the screens' constructor and pass the navigator in props.
constructor(props) {
super(props);
this.props.navigator.setButtons(this.navigatorButtons(this.props.navigator));
}
navigatorButtons = (navigator) => {
return {
leftButtons: [
{
id: 'custom-button',
component: 'CustomButton',
passProps: {
text: 'Hi!',
navigator
}
}
]
};
}
Don't forget that custom left button isn't supported on Android.

Related

The color of the navlinks doesn't change even though the state changes to dark mode

I am using react for the first time, and I am facing this error. (Sorry for this mess of a code)
App.js
const btnMode = {
lightBtn: <LightModeIcon style={{color: "white", marginRight: "1.25rem",}} />,
darkBtn: <NightsStayIcon style={{color: "#395B64", marginRight: "1.25rem",}} />,
}
const [linkStyle, setLinkStyle] = useState({
color: "black",
});
const [btnStyle, setBtnStyle] = useState(btnMode.darkBtn);
const themeHandler = () => {
if(btnStyle.type.type.render.displayName === "NightsStayIcon") //to change from light to dark mode
{
setBtnStyle(btnMode.lightBtn);
setLinkStyle({
color: "white !important",
});
}
else{
setBtnStyle(btnMode.darkBtn);
setLinkStyle({
color: "black",
});
}
}
return(
<div className="App">
<Header style={linkStyle}/>
<div onClick={themeHandler}>
{ btnStyle }
</div>
</div>
);
I sent the state of the linkStyle to Header component which should change accordingly to the state of the btnStyle.
Header.js
function Header(props) {
console.log(props.style) // {color: "white !important"} when state changes accordingly
const navLinks = [
{ id: 0, body: "Home", link: "/" },
{ id: 1, body: "About", link: "/about" },
{ id: 2, body: "Articles", link: "/articles" },
{ id: 3, body: "Social", link: "/social" },
{ id: 4, body: "Contact", link: "/contact" },
];
return (
<div className="navContent">
{navLinks.map((item) => (
<a href={item.link} key={item.id} style={props.style}>
{item.body}
</a>
))}
</div>
)
In a tag, I cant get the style required even though I can see that in the console, the property does reflect the state change. What am I doing wrong here?
you are not calling the function themeHandler() when you want to change the theme.
ideally you should call this function as well whenever you want to switch to the theme.

ReactJs Accordion Automatic Close Mechanism

I'm currently working on an accordion component in react version 16.3.2, that receives props from another component and displays the accordion accordingly. Unfortunately I cannot use hooks.
This works pretty smoothly, what I want to implement now is a way so that only one accordion can be opened at the same time on the same page. So, once you open one accordion (while another is already opened) it should automatically close the already opened one.
I have an Id (string which describes the current section, e.g 'contact', 'info', etc.) and the state of an accordion gets saved in the state (set to true when you toggle the accordion).
I'm not quite sure on how I could implement this mechanism and am looking for tips on how I could solve this in a smart way. Any pointers?
example:
https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-6dys1
(I didn't add all of the styling, animations since this is more about functionality)
You could do something like this, using the state hook in the App component
export default function App() {
const items = [
{ id: 1, title: 'First Accordion', content: 'Hello' },
{ id: 2, title: 'Click me', content: 'Hello 2' },
{ id: 3, title: 'Third Accordion Accordion', content: 'Hello 3' },
]
const [selectedItem, setSelectedItem] = useState(1)
const handleClick = id => {
setSelectedItem(id)
}
return (
<div className="App">
{items.map(x => {
return (
<Accordion
key={x.id}
id={x.id}
title={x.title}
open={x.id === selectedItem}
onClick={handleClick}
>
<p>{x.title}</p>
</Accordion>
)
})}
</div>
);
}
Then your accordion component is a bit simpler
class Accordion extends React.Component {
accToggle() {
this.props.onClick(this.props.id);
}
sectionClasses() {
let classes = "accordion";
classes += this.props.open ? " sec-on" : "";
classes += "sec-underway";
return classes.trim();
}
render() {
return (
<section className={this.sectionClasses()} id={this.props.id}>
<div className="acc-title" onClick={this.accToggle.bind(this)}>
<h3 className="acc-text">{this.props.title}</h3>
<div className="acc-nav">
<span className="acc-toggle" />
</div>
</div>
<div className="acc-content">{this.props.children}</div>
</section>
);
}
}
Accordion.defaultProps = {
open: false
};
Accordion.propTypes = {
id: PropTypes.number.isRequired,
children: PropTypes.any,
onFocus: PropTypes.func,
progress: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
PropTypes.bool
]),
title: PropTypes.string,
open: PropTypes.bool
};
export default Accordion;
The accordion calls a function on the app component, which updates the state and the display logic is passed down in the props
You can find solution for your problem in the following codesandbox
https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-yejc0
Change prop names as it fits your code base, but the logic is solid

How do I make sure my menu refreshes when a new user logs in? Trying to show the proper items (admin navigation for admin users)

My SideMenu.js basically does a check to see if user is admin or not before building the proper routes to render, my issue is that when i sign out and sign back in, it keeps the same menu from the previous user.
It looks something like this:
class SideMenu extends Component {
constructor () {
super();
this.state = {
isAdmin: false,
isLoading: true,
footer: [
{ label: 'Settings', url: 'settingsUrl', icon: settingsIcon, id: '1' },
{ label: 'Log Out', url: 'logOutUrl', icon: signoutIcon, id: '2' },
]
}
this.checkVerification();
}
checkVerification = () => {
var isLoggedIn = Firebase.isLoggedIn();
var isAdmin = Firebase.isAdmin();
Promise.all([ isLoggedIn, isAdmin ]).then((responses) => {
isLoggedIn = responses[0];
isAdmin = responses[1];
if(isLoggedIn){
if(isAdmin){
this.setState({
isAdmin: true,
isLoading: false,
routes: [
{ label: 'Screen1', url: 'screenUrl1', icon: screenIcon1, id: '1' },
{ label: 'Screen2', url: 'screenUrl2', icon: screenIcon2, id: '2' },
{ label: 'AdminScreen3', url: 'screenUrl3', icon: screenIcon3, id: '3' },
]
})
}else{
this.setState({
isLoading: false,
routes: [
{ label: 'Screen1', url: 'screenUrl1', icon: screenIcon1, id: '1' },
{ label: 'Screen2', url: 'screenUrl2', icon: screenIcon2, id: '2' },
]
})
}
}else{
this.props.navigation.navigate('Login')
}
})
}
Then I render the menu at the end:
render () {
const { isLoading } = this.state;
return(
<View style={styles.container}>
{isLoading && this.renderLoading()}
{!isLoading && this.renderMenu()}
</View>
)
}
The Menu only loads once and never refreshes when a user signs out and back in with a different account. I tried a bunch of checks in the sidemenu render but it resulted in functions being spammed and didnt work either way. Then I found a post of someone mentioning to use StackActions.reset, which I did in my Login Screen, which works, the code is as follows:
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Init' })],
});
this.props.navigation.dispatch(resetAction);
This works, but I get the warning:
'Warning: Can\'t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s', 'the componentWillUnmount method', '\n in Login (at SceneView.js:9)
What's the proper way to go about refreshing my menu on new user? I've been trying to fix it for quite a while with no proper result. I'm not sure if it helps to know that I load my SideMenu from my Init Page:
import { AppRegistry, Dimensions, YellowBox } from 'react-native';
import { createDrawerNavigator , createAppContainer } from 'react-navigation';
import SideMenu from './pages/menu/SideMenu'
import Routes from './Routes';
const DrawerNav = createDrawerNavigator({
Screen: {
screen: Routes,
}
}, {
contentComponent: SideMenu,
drawerWidth: Dimensions.get('window').width/1.7,
overlayColor: 'rgba(0, 0, 0, 0.7)',
minSwipeDistance: 5,
});
const Init = createAppContainer(DrawerNav);
export default Init;
Any help would be greatly appreciated, thank you in advance.
I figured it out. First of all I had a bunch of promises that weren't getting resolved when signing out that were giving me errors, after resolving them properly, I ended up changing my logOut.js to:
componentDidMount(){
Firebase.logOut().then((response) => {
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Login' })],
});
this.props.navigation.dispatch(resetAction);
})
}
That did the trick for me, when I tried that in my SideMenu.js when route == 'LogOut' it gave me warnings. It seems to be working this way though. Hope this helps someone struggling with the same issue out if they come across this.

How to change the color of Material-UI's Toggle

So I put my Toggle button in my AppBar, which created an issue because they are the same color when the Toggle is selected.
I've tried many different things (as shown below), but have been unable to change it's color.
import React from 'react';
import Toggle from 'material-ui/Toggle'
import Drawer from 'material-ui/Drawer';
import AppBar from 'material-ui/AppBar';
import MenuItem from 'material-ui/MenuItem';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
var Style =
{
palette:
{
primary1Color: '#ffffff',
},
};
class AppBarComp extends React.Component {
constructor() {
super();
this.state = {
open: false
};
}
getChildContext() {
return {muiTheme: getMuiTheme(Style)};
}
handleToggle = () => this.setState({open: !this.state.open});
handleClose = () => this.setState({open: false});
render() {
return <MuiThemeProvider muiTheme={getMuiTheme()}>
<div>
<AppBar
onLeftIconButtonTouchTap={this.handleToggle}
title="Time Visualizer"
iconElementRight={
<Toggle
labelStyle={{color:'white'}}
style={{marginTop:'.75em'}}
label="Toggle Compare"
/>
}/>
<Drawer
docked={false}
width={250}
open={this.state.open}
onRequestChange={(open) => this.setState({open})}
>
<MenuItem onTouchTap={this.handleClose}>Settings</MenuItem>
<MenuItem onTouchTap={this.handleClose}>About</MenuItem>
</Drawer>
</div>
</MuiThemeProvider>
}
}
AppBarComp.childContextTypes ={
muiTheme: React.PropTypes.object,
};
export default AppBarComp;
I'm not really sure how I can get to that element to change it's color. using Chrome, I was able to inspect the element and change it's color that way, but have been unable to repeat that with code.
I've also been unable to center the Toggle programmatically, but have been able to do it in chrome which makes be believe I'm not high enough in the object?
If that makes sense.
Thanks!
If you want change toggle color in 'on mode', you need to update colors in the theme:
const muiTheme = getMuiTheme({
toggle: {
thumbOnColor: 'yellow',
trackOnColor: 'red'
}
});
and then use it :)
<MuiThemeProvider muiTheme={muiTheme}>
You can check here what other theme stuff is used by toggle:
https://github.com/callemall/material-ui/blob/master/src/Toggle/Toggle.js
I don't know if that is the only way to do this but it seems to work :)
There might be problem though if some other control uses that color path :/
Changing color of toggle in 'off mode' is easier:
<Toggle
thumbStyle={{ backgroundColor: 'red' }}
trackStyle={{ backgroundColor: 'green' }} />
Hope it helps :)
import {Switch,makeStyles} from "material-ui/core"
const useStyles = makeStyles((theme) => ({
toggle: {
width:50,
'& .Mui-checked': {
color: '#109125',
transform:'translateX(25px) !important'
},
'& .MuiSwitch-track': {
backgroundColor:'#008000e0'
}
},
})
const Index= (props) => {
const classes = useStyles();
return(
<Switch color="primary" size="small" className={classes.toggle} checked: {true} />)
}
Refer to this code and you will get what you need.
Click on this link for more information Material-Ui/Switch
All you need to do
thumbSwitchedStyle={{ backgroundColor: 'grey' }}
Example
<Toggle
thumbSwitchedStyle={{ backgroundColor: 'grey' }}
labelStyle={{color:'white'}}
style={{marginTop:'.75em'}}
label="Toggle Compare"
Thus, if selected the color becomes grey :)
image
const toggleStyles = makeStyles({
root: { /* … */ },
label: { /* … */ },
outlined: {
/* … */
'&$disabled': { /* … */ },
},
outlinedPrimary: {
/* … */
'&:hover': { /* … */ },
},
disabled: {},
}, { name: 'MuiButton' });
generates the following class names that you can override:
.MuiButton-root { /* … */ }
.MuiButton-label { /* … */ }
.MuiButton-outlined { /* … */ }
.MuiButton-outlined.Mui-disabled { /* … */ }
.MuiButton-outlinedPrimary: { /* … */ }
.MuiButton-outlinedPrimary:hover { /* … */ }
To use the code:
function FunctionalComponent(props){
const toggleClass = toggleStyles();
return (
<ToggleButtonGroup value={toggleValue} onChange ={handleToggleValueChange}>
<ToggleButton value="toggleValue1" className={toggleClass.root}>VALUE 1</ToggleButton>
<ToggleButton value="toggleValue2" className={toggleClass.outlined}>VALUE 2</ToggleButton>
</ToggleButtonGroup>
)
}
For more details: https://material-ui.com/styles/advanced/#with-material-ui-core
The color of the Material-UI toggle is set to primary1Color, which you can over-ride by making a custom theme.
http://www.material-ui.com/#/customization/themes
You'd want to make a styles object somewhere (a separate file is probably best) that contains an object like this:
{
palette: {
primary1Color: '#YOURCOLOR',
},
}
Assuming you import that into your React class as Styles, you'd want to set it as the theme like this:
getChildContext() {
return {
muiTheme: getMuiTheme(Styles),
};
}
And
YOUR_CLASS.childContextTypes = {
muiTheme: React.PropTypes.object,
};

Material-ui transition between old LeftNav menu style and the composability one with Reactjs

Ok, so I am very confused about the way of making a LeftNav menu with material-ui.
I am new on the project and I updated reactjs and material-ui.
So, a lot of stuff have been deprecated about LeftNav from material-ui and I am trying to fix it.
Here is the menu as it was when I opened the project (with all the console warning):
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
menuItems={menuItems}
onChange={this.leftNavOnChange} />
From this array:
var menuItems = [
{ route: '/', text: 'Home' },
{ type: 'SUBHEADER', text: 'Connect' },
{ route: '/categories', text: 'Categories' },
{ route: '/icons', text: 'Icons'},
{ route: '/Tmp', text: 'Tmp', disabled: !Permissions['connect_v2_list_tmp']['isPermitted'] },
{ route: '/wizard', text: 'Wizard', disabled: !Permissions['connect_v2_analyze_spreadsheet']['isPermitted'] },
{ route: '/linkshortener', text: 'Link shortener'},
{ type: 'SUBHEADER', text: 'Visual search' },
{ route: '/whitelist', text: 'Whitelist', disabled: !Permissions['connect_v2_list_whitelist']['isPermitted'] },
{ route: '/blacklist', text: 'Blacklist', disabled: !Permissions['connect_v2_list_blacklist']['isPermitted'] },
{ type: 'SUBHEADER', text: 'Tmp-wise' },
{ route: '/viewer', text: 'Viewer', disabled: !Permissions['connect_v2_view_bw_entity']['isPermitted']},
];
And here is what I did from what I understood about the way of doing it:
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
//menuItems={menuItems}
//onChange={this.leftNavOnChange}
>
{menuItems.map(function(items, i) {
if (items.route) {
return <MenuItem linkButton={true} href={items.route} key={i}>{items.text}</MenuItem>;
} else {
return <MenuItem data={items.type} key={i}>{items.text}</MenuItem>;
}
})}
</LeftNav>
So, less warning except one : using methods on left nav has been deprecated. Please refer to documentations.
but not such a big deal.
My problem here, is that my links are not working. I am staying on the same page.
And my other main problem: all the style it had is gone.
So, my question is:
am I doing it right?
Or am I missing something owned by reactjs and / or material-ui?
Thanks a lot in advance for the time spent on my request.
This is what I do (I am using react-router):
import { browserHistory } from 'react-router';
handleLeftNav: function (route) {
browserHistory.push(route);
this.setState({
leftNavOpen: false
});
},
<MenuItem onTouchTap={() => { return this.handleLeftNav('/route/'); }}>Route</MenuItem>
If you move your map outside of the LeftNav then you should no longer receive this warning. When I compose my LeftNav I follow this pattern and I don't get the error you're reporting. Hope this helps.
let menuItems = menuItems.map(function(items, i) {
if (items.route) {
return <MenuItem linkButton={true} href={items.route} key={i}>{items.text}</MenuItem>;
} else {
return <MenuItem data={items.type} key={i}>{items.text}</MenuItem>;
}
});
<LeftNav ref="leftNav"
docked={false}
style={{opacity: '0.9'}}
//menuItems={menuItems}
//onChange={this.leftNavOnChange}
>
{ menuItems }
</LeftNav>

Categories

Resources