I have a functional component Profile.js and on the header of that screen I put a button. Once this button is pressed I want to display a dropdown menu from react-native-material-menu
Part of my Profile.js:
function Profile(props) {
useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => <HeaderRight />,
});
}
}, []);
My HeaderRight component:
export const HeaderRight = () => {
return (
<TouchableOpacity style={{ marginTop: 20, marginRight: 8 }}>
<Feather
name="more-vertical"
size={24}
color="black"
onPress={() => renderMenu()}
/>
</TouchableOpacity>
);
};
As you can see when the icon is pressed I call my renderMenu(), which looks like the following:
import React, { useState } from "react";
import { View, Text } from "react-native";
import { Menu, MenuItem, MenuDivider } from "react-native-material-menu";
export const renderMenu = (props) => {
const [visible, setVisible] = useState(true);
const hideMenu = () => setVisible(false);
const showMenu = () => setVisible(true);
return (
<View
style={{ height: "100%", alignItems: "center", justifyContent: "center" }}
>
<Menu
visible={visible}
anchor={<Text onPress={showMenu}>Show menu</Text>}
onRequestClose={hideMenu}
>
<MenuItem onPress={hideMenu}>Menu item 1</MenuItem>
<MenuItem onPress={hideMenu}>Menu item 2</MenuItem>
<MenuItem disabled>Disabled item</MenuItem>
<MenuDivider />
<MenuItem onPress={hideMenu}>Menu item 4</MenuItem>
</Menu>
</View>
);
};
When I render my page, I click on the header icon and I get the following error:
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Can you tell me, how do I render my dropdown menu component?
You do not render the JSX on the return of an onPress function. Try this out:
export const HeaderRight = () => {
const [visible, setVisible] = useState(false);
let toggle = () => setVisible(!visible);
return (
<>
<TouchableOpacity style={{ marginTop: 20, marginRight: 8 }}>
<Feather
name="more-vertical"
size={24}
color="black"
onPress={toggle}
/>
</TouchableOpacity>
<Menu
visible={visible}
anchor={<Text onPress={toggle}>Show menu</Text>}
onRequestClose={toggle}>
<MenuItem onPress={toggle}>Menu item 1</MenuItem>
<MenuItem onPress={toggle}>Menu item 2</MenuItem>
<MenuItem disabled>Disabled item</MenuItem>
<MenuDivider />
<MenuItem onPress={toggle}>Menu item 4</MenuItem>
</Menu>
</>
);
};
Related
I have a code with which the user can select a file from their device. The Card component will display its name and operations that can be done with the file.
But my problem is that I don't know how to close this component if the user wants to cancel the action.
export default function DisplaySelectedFile() {
const [fileName, setFileName] = useState("");
console.log(setFileName)
return (
<div>
<SelectFileButton setFileName={setFileName} />
{fileName && <Card sx={styles.CommonStyle}>
<Stack spacing={10} direction="row" style={{paddingTop: "20px", paddingLeft: "10px"}}>
<div>{fileName}</div>
<Stack spacing={3} direction="row">
<div>Convert to</div>
<ConvertToFormatFile></ConvertToFormatFile>
</Stack>
<Button>CONVERT</Button>
<CloseIcon/>
</Stack>
</Card>}
</div>
);
}
I have added a button which should close the entire Card component. If I add the following code
<CloseIcon onClick={() => setFileName(false)}/>
If I add the following code, then the component closes. But the next time you select a file, this component does not open (only after reloading the page).
Tell me how to close the Card component correctly so that you can open it without problems
I would suggest to handle separately the card visibility and the file name value.
Something like this should work:
import React, { useState, useCallback } from "react";
const DisplaySelectedFile = () => {
const [fileName, setFileName] = useState(null);
const [showCard, setShowCard] = useState(false);
const handleSelectFile = useCallback(
(file) => {
setFileName(file);
file && setShowCard(true);
},
[setFileName, setShowCard]
);
const handleCloseCard = useCallback(() => {
setShowCard(false);
setFileName(null); // add this line only if it fits your use case
}, [setFileName, setShowCard]);
return (
<div>
<SelectFileButton setFileName={handleSelectFile} />
{showCard && (
<Card sx={styles.CommonStyle}>
<Stack
spacing={10}
direction="row"
style={{ paddingTop: "20px", paddingLeft: "10px" }}
>
<div>{fileName}</div>
<Stack spacing={3} direction="row">
<div>Convert to</div>
<ConvertToFormatFile></ConvertToFormatFile>
</Stack>
<Button>CONVERT</Button>
<CloseIcon onClick={handleCloseCard} />
</Stack>
</Card>
) || null}
</div>
);
}
export default DisplaySelectedFile;
I am working on a hobby gym management app, and I am puzzled by the mechanism of sharing state between three components in React-Native.
My three components are:
1. Schedule:
[...]
function Schedule() {
return (
<Stack.Navigator
initialRouteName="Monday"
screenOptions={{
headerStyle: { backgroundColor: "#f58220" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
headerRight: () => <SwitchButton />,
}}
>
<Stack.Screen
name="TabStack"
component={TabStack}
options={{ title: "Aerobic Schedule" }}
/>
</Stack.Navigator>
);
}
export default Schedule;
I want the SwitchButton button in my Schedule component (1.) to alternate between DATA_AEROBIC and DATA_KIDS arrays props of the FlatList in (2.) based on the content of the listAerobic boolean variable.
2. MondayPage:
[...]
const MondayPage = () => {
const [selectedId, setSelectedId] = useState(null);
const [listAerobic, setListAerobic] = useState(true);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6e3b6e" : "#f9c2ff";
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
style={{ backgroundColor }}
/>
);
};
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={{ flex: 1, padding: 5 }}>
<SafeAreaView style={styles.container}>
<FlatList
data={listAerobic ? DATA_AEROBIC : DATA_KIDS}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
</SafeAreaView>
</View>
</SafeAreaView>
);
};
However, I don't know how to link the listAerobic boolean variable to the state of the SwitchButton component (3.) , and how to make it toggle on and off.
3. SwitchButton:
const SwitchButton = () => {
const [isEnabled, setIsEnabled] = useState(false);
const toggleSwitch = () => setIsEnabled(previousState => !previousState);
return (
<View style={styles.container}>
<Switch
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
ios_backgroundColor="#3e3e3e"
onValueChange={toggleSwitch}
value={isEnabled}
/>
<Text> aerobic/kids</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
marginRight: 5,
padding: 5,
}
});
export default SwitchButton;
Any guidance would be awesome! I mention I have really tried to look it up on different tutorials, but I can't seem to get the gist of it. It is my first project in React/React-Native.
Many thanks!
I think you just need 'value' to accept a prop passed into it on the switch button. Then wherever you use switch button just pass a boolean value into it from state e.g.
<SwitchButton enabled={this.state.switchEnabled}/>
As for setting state 'globally' so this.state.switchEnabled can be updated from various places / accessible all over the app you need to look into state management tools like Redux (or I hear 'React Hooks' is now a thing and preferred....)
I want to be able to use navigation on a different screen other than just the first one but I am getting an error that this.props does not exist.
I have my App.js file setup like this:
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
import Screen2 from './screens/Screen2';
import Screen3 from './screens/Screen3';
import HomeScreen from './screens/HomeScreen';
const Stack = createStackNavigator();
function HomeScreen({ navigation }) {
return (
<View>
<Button
title="Go to Screen2"
onPress={() => {
navigation.navigate('Screen2');
}}
/>
<Button
title="Go to Screen3"
onPress={() => {
navigation.navigate('Screen3');
}}
/>
</View>
);
const App: () => React$Node = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Screen2" component={Screen2} />
<Stack.Screen name="Screen3" component={Screen3} />
</Stack.Navigator>
</NavigationContainer>
);
};
The buttons in app.js work but if I go to Screen2 and click a button that intends to go to another (Screen3 in the example below), props does not exist.
Example Screen2.js would look like this:
const Screen2: () => React$Node = () => {
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3}}
/>
</View>
</>
);
function goToScreen3() {
if(condition){
this.props.navigate.navigate('Screen3');
}}
How do I pass the props so that I can use navigation in my second screen?
For functional component sometimes it's tricky to pass navigation through props as well. So just use withNavigation.
you have to import it and wrap the function with it.
import { withNavigation } from 'react-navigation';
const Screen2 = props => {
const goToScreen3 = () => {
if(condition){
props.navigate.navigate('Screen3');
}}
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3()}
/>
</View>
</>
);
export default withNavigation(Screen2)
In Functional Component there is no this binding so you need to get the props from the function first
check th
const Screen2 = (props) => {
return (
<>
<View style={{ flex: 1 }}>
<Button
title="Go to Screen3"
onPress={goToScreen3}}
/>
</View>
</>
);
function goToScreen3() {
if(condition){
props.navigate.navigate('Screen3');
}
}
}
I have a tab component in Material-UI and I want to implement a tooltip on it.
My problem is that when I click the tab component, the tooltip is not disappearing. It must disappear after I click on that tab.
Currently, it continues to be visible even after I click on the tab.
How do I rectify that?
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ToolTip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ToolTip>
);
}
)}
</Tabs>
If you look at the document of Material-UI tooltip API
You would find a props named disableHoverListener
bool
default: false
Do not respond to hover events.
Set it as True would turn off the tooltip onMouseOver event trigger.
Update
Or you can simply make it totally under control.
By binding the onClick, onMouseOver, onMouseLeave, open to related component.
import React, { useState } from "react";
import "./styles.css";
import { Tooltip, Tab } from "#material-ui/core";
export default function App() {
const [flg, setFlg] = useState(false);
const [isHover, setIsHover] = useState(false);
return (
<div className="App">
<Tooltip
title={"message"}
aria-label="add"
placement="bottom"
open={!flg && isHover}
>
<Tab
label={`Click: ${!flg ? "enabled" : "disabled"}`}
onClick={() => setFlg(!flg)}
onMouseOver={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
/>
</Tooltip>
</div>
);
}
Try it online:
You can also implement a generic tooltip with a managed state when to open/close the tooltip.
import Tooltip, { TooltipProps } from "#mui/material/Tooltip";
import { useState } from "react";
/**
* MUI Tooltip wrapper with adaption to the move away once focuses left.
*/
export function ManagedTooltip(props: TooltipProps) {
const [open, setOpen] = useState<boolean>(false);
// Wrap Tooltip with div to capture mouse events
return <div style={{ display: 'flex' }}
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
onClick={() => setOpen(false)}
>
{/* Show the original MUI Tooltip with all props. */}
{/* Just override the open attribute to be fully managed, and disable internal listeners */}
<Tooltip {...props} open={open} disableHoverListener disableFocusListener />
</div>;
}
Once it's ready, you can use it anywhere exactly like the original MUI tooltip.
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ManagedTooltip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ManagedTooltip>
);
}
)}
</Tabs>
The way I solved this was by rendering the tooltip conditionally. In your case I suppose you want the tooltip not to render for the tab of the current active route:
function ConditionalTooltip({renderTooltip, children, ...props}) {
return renderTooltip ? <Tooltip {...props}>{children}</Tooltip> : children;
}
function Tabs() {
const location = useLocation();
return (
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ConditionalTooltip
renderTooltip={location.pathname.indexOf(title) === -1} /* only render tooltip on not active urls */
title={description}
>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ConditionalTooltip>
);
}
)}
</Tabs>
)
}
I'm calling a component in my file so I made a function in parent component and want to hit this function onPress of child component MenuItem and I also want to hit hideMenu function from child component at same time. Please provide me a solution for it. Thanks
Parent Component
onView(){
alert('Dairu');
}
{this.state.clientsList.map((item) => {
return (
<View style={styles.caseItem} key={item.ID}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<CustomMenu onView={() => this.onView()} />
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
})}
Child Component
hideMenu = () => {
this._menu.hide();
};
render() {
return (
<Menu
ref={this.setMenuRef}
button={<Icon type="Feather" name="more-vertical" onPress={this.showMenu} style={{ fontSize: 20, color: '#555' }} />}
>
<MenuItem onPress={this.props.onView}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
);
}
You've already passed the onView prop to the child object. Just call this method in the hideMenu method in the child component:
hideMenu = () => {
this._menu.hide();
this.props.onView();
};
you just need to add this.props.onView() on your hideMenu function
hideMenu = () => {
this._menu.hide();
this.props.onView();
};
also, you can pass an argument to parent component like:
this.props.onView(arg1, arg2);
and in parent component:
onView={(arg1, arg2)=> onView(arg1, arg2)}