Component composition issue react - reactjs

I'm trying to use composition component for making a menu in react native but I can't figure it why it doesn't work as expected.
I use [react native pop menu][1] to make the menu. Here what I'm trying to do :
Make a MenuContainer component with a children property
Make a MyMenuOptions component that will be nested inside the menu container as children.
import in my screen MyMenuOptions nested in MenuContainer
Issue: the menu container is working as expected but the MyMenuOptions is not showing on screen.
[1]: https://github.com/instea/react-native-popup-menu
MyMenuContainer component :
const MenuContainer = ({ nameMenu, children }) => {
const optionsStyles = {
optionsContainer: {
padding: 3,
width: 250,
},
};
return (
<Menu style={{ flexDirection: "row", marginRight: 10 }}>
<MenuTrigger
style={styles.triggerMenu}
text={nameMenu}
customStyles={{
TriggerTouchableComponent: TouchableWithoutFeedback,
}}
/>
<MenuOptions customStyles={optionsStyles}>{children}</MenuOptions>
</Menu>
);
};
MyMenuOptions Component :
import React from "react";
import { StyleSheet, Text } from "react-native";
import { MenuOption } from "react-native-popup-menu";
import MenuContainer from "./MenuContainer";
import { CheckBox } from "react-native-elements";
const MyMenuOptions = ({ textSubMenu, value, toggle }) => {
return (
<MenuContainer>
<MenuOption onSelect={toggle} style={styles.sousMenu}>
<CheckBox
center
title={textSubMenu}
checkedIcon="dot-circle-o"
uncheckedIcon="circle-o"
checked={value}
/>
</MenuOption>
</MenuContainer>
);
};
styles....
export default MyMenuOptions;
And in my final screen I import both component :
import MenuContainer from "../components/Menu/MenuContainer";
import MyMenuOptions from "../components/Menu/MenuCheck";
Some code ....
<View style={styles.menuOnglet}>
<MenuContainer nameMenu="Test menu">
<MyMenuOptions textSubMenu="My sub menu" value={true} />
</MenuContainer>
</View>
So as I said I can see the "Test menu" button show on my screen but i see nothing inside the menu when i click on it like there is no children.
Please, what am I missing here ?

why you surround MenuOption with MenuContainer in MyMenuOptions.js
const MyMenuOptions = ({ textSubMenu, value, toggle }) => {
return (
<MenuContainer> <!----------- you need to remove this -->
<MenuOption onSelect={toggle} style={styles.sousMenu}>
<CheckBox/>
</MenuOption>
</MenuContainer> <!---------- you need to remove this -->
);
};
you steps should be :
App.js file
it must be like this
import { MenuProvider } from 'react-native-popup-menu';
export const App = () => (
<MenuProvider>
<YourApp />
</MenuProvider>
);
MenuContainer.js file
const MenuContainer = ({ nameMenu, children }) => {
return (
<Menu >
<MenuTrigger/>
<MenuOptions>
{children}
</MenuOptions>
</Menu>
);
};
MyMenuOptions.js file
const MyMenuOptions = ({ textSubMenu, value, toggle }) => {
return (
<MenuOption onSelect={toggle} style={styles.sousMenu}>
.......
</MenuOption>
);
};

Related

How to check if my react-native-modalize modal is open or close?

Actually i am working on multiple modal at once so i have to make a condition that if my modal with "xyz" ref is close then display a button.
react-native-modalize has onOpen and onClose props which will allow you to set some state to keep track of then open/closed status of each modal. You can then use that state to control the rendering of your buttons.
Example on Expo Snack.
import React, { useRef } from 'react';
import { View, Text, Button } from 'react-native';
import { Modalize } from 'react-native-modalize';
import RnGH from 'react-native-gesture-handler';
const { useState, useEffect } = React;
export default function App() {
const modalA = useRef(null);
const modalB = useRef(null);
const [modalAOpen, setModalAOpen] = useState(false);
const [modalBOpen, setModalBOpen] = useState(false);
return (
<>
<Button
onPress={() => {
if (modalAOpen) {
modalA.current?.close();
} else {
modalA.current?.open();
}
}}
title={`${modalAOpen ? 'Close' : 'Open'} Modal A`}
/>
<Button
onPress={() => {
if (modalBOpen) {
modalB.current?.close();
} else {
modalB.current?.open();
}
}}
title={`${modalBOpen ? 'Close' : 'Open'} Modal B`}
/>
<Modalize
ref={modalA}
onOpen={() => setModalAOpen(true)}
onClose={() => setModalAOpen(false)}>
<Text style={{ color: 'red' }}>Modal A</Text>
</Modalize>
<Modalize
ref={modalB}
onOpen={() => setModalBOpen(true)}
onClose={() => setModalBOpen(false)}>
<Text style={{ color: 'blue' }}>Modal B</Text>
</Modalize>
</>
);
}
You should make a custom component with a modal in it, so every modal has its own state - isOpen. Then in your custom modal component, you can show something or not, based on your isOpen property.

Radio button value not resetting

I have a list of questions. And on click of next or previous I'm changing the question. I have 4 or 5 answers as radio buttons. I' using this package Radio button npm. And now I'm trying to change the question using states. I need the radio button unselected on changing of question. All content changing easily expect radio button resetting. Here is my code:-
<ScrollView>
<RenderHtml
contentWidth={width}
source={{html:question}}
/>
<OptionsView options={options} selectedBtn={(e) =>updateAnswer(e.option)}/>
</ScrollView>
Functional component
const OptionsView=(props)=>{
return(
<RadioButtonRN
style={{marginHorizontal:5,marginTop:1,margin:5}}
deactiveColor={'gray'}
activeColor={'green'}
initial={0}
boxStyle={{height:50}}
data={props.options}
icon={
<Icon
name="check-circle"
size={25}
color="green"
/>
}/>
)}
I solved it using below code:-
import React, { useState } from "react";
import { View, StyleSheet, Button, Alert } from "react-native";
import RadioButtonRN from 'radio-buttons-react-native';
const App = () => {
const [show,setShow] = React.useState(true);
const data = [
{
label: 'data 1'
},
{
label: 'data 2'
}
];
React.useEffect(()=>{
if(!show) setShow(true)
},[show])
const resetHandler = () =>{
setShow(false)
}
return (
<View style={styles.container}>
{show &&
<RadioButtonRN
data={data}
selectedBtn={(e) => console.log(e)}
/>
}
<Button title='reset' onPress={resetHandler} />
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingTop:100,
}
});
export default App;
Reference:- Radio Reset Button
If you are rendering your options base on the props that has been passed from parent component , U need to also include this hook in order to update your child component:
const OptionsView = (props.options) => {
const [options, setOptions] = useState(props.options);
useEffect(() => {
setOptions(props.options)
}, [props.options]
return (
<RadioButtonRN/>
)
}

Navigation with parameters from custom element in Flatlist in React Native: Error: Invalid hook call

I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
ClassA should have a checkbox with state handling and a flatlist containing CustomButton
Navigate from ClassA to TargetScreen by clicking CustomButton
Pass parameter "element" to TargetScreen
Show content of parameter passed in TargetScreen
The error message I get:
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 See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and
fix this problem.
ClassA:
import React, { Component, useState } from 'react';
import { useNavigation } from '#react-navigation/native';
import { CustomButton} from './CustomButton.js';
import { CheckBox, SafeAreaView, FlatList} from 'react-native';
class ClassA extends React.Component {
render() {
const [ASelected, setA] = useState(false);
const NavigateWithParams = () => {
navigation = useNavigation();
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => navigateWithParams()} />
);
}
return (
<CustomConst/>
<CheckBox value={ASelected}
onValueChange={{setA}} />
<SafeAreaView>
<FlatList
data={data}
renderItem={renderItemCustom}
keyExtractor={(item) => item.element}
/>
</SafeAreaView>
);
}
export default ClassA;
TargetScreen:
class TargetScreen extends React.Component {
render() {
const { navigation } = this.props;
return (
<Text> {JSON.stringify(navigation.getParam('element'))} </Text>
);
}
}
export default TargetScreen;
+++++++++++++++++++++++++
Update:
As of now the code looks like this:
class ClassA extends React.Component {
NavigateWithParams = (element) => {
this.props.navigation.navigate('TargetScreen', { element: 'elementname' })
}
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => this.NavigateWithParams(item.element)} />
);
render() {
return (
<SafeAreaView>
<FlatList
data={data}
renderItem={this.renderItemCustom}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
}
}
export default ClassA;
And I am now getting this issue:
TypeError: Cannot read property 'navigate' of undefined
+++++++++++++++++++++++++
Update2
Routing:
function ClassA({ navigation }) {
return (
<ClassAScreen/>
);
function Target({ navigation }) {
return (
<TargetScreen/>
);
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="ClassA"
component={ClassA}
options={{ tabBarLabel: 'ClassA!', headerShown: false }}
/>
<SessionStack.Screen
name="Target"
component={Target}
options={{ tabBarLabel: 'works!' }}
/>
</SessionStack.Navigator>
)
}
Logging gives me this:
renderItemCustom = ({ item }) => (
<CustomButton element={item.title} onPress={() => console.log(this.props)} />
);
+++++++++++++++++
Update:
Solution can be found here:
Navigation with parameters from custom element in Flatlist in React Native: Empty parameters
You cant use hooks inside a class component so remove the line which has the hook
and change like below
const NavigateWithParams = element => {
this.props.navigation.navigate('TargetScreen', { element: element })
}
const renderItemCustom= ({ item }) => (
<CustomButton onPress={() => this.navigateWithParams(item.element)} />
);
And parameter are passed using the route prop
class TargetScreen extends React.Component {
render() {
const { route} = this.props;
return (
<Text> {JSON.stringify(route.params.element)} </Text>
);
}
}
Also for the checkbox instead of using the useState hook, use this.state.
You can’t use Hooks inside a class component
https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
UPDATE:
Work variant, you can try here: https://snack.expo.io/#vasylnahuliak/stackoverflow-67862370
import 'react-native-gesture-handler';
import React from 'react';
import { Text, View, StyleSheet, Button, FlatList } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const DATA = [
{
id: 0,
title: 'first button',
element: 'something'
},
{
id: 1,
title: 'second button',
element: 'another something'
},
]
const HomeSceen = ({ route, navigation }) => {
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<Button
title="Navigate to ProfileScreen"
onPress={() => {
navigation.navigate('ProfileScreen');
}}
/>
</View>
);
};
const ProfileScreen = ({ route, navigation }) => {
const NavigateWithParams = (element) => {
navigation.navigate('HomeSceen', { element });
};
const renderItemCustom = ({ item }) => (
<Button title={item.title} onPress={() => NavigateWithParams(item.element)} />
);
return (
<View>
<Text>{JSON.stringify(route, null, 2)}</Text>
<FlatList
data={DATA}
renderItem={renderItemCustom}
keyExtractor={(item) => item.id}
/>
<Button
title="Navigate to HomeSceen"
color="tomato"
onPress={() => {
navigation.navigate('HomeSceen');
}}
/>
</View>
);
};
const SessionStack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<SessionStack.Navigator>
<SessionStack.Screen name="HomeSceen" component={HomeSceen} />
<SessionStack.Screen name="ProfileScreen" component={ProfileScreen} />
</SessionStack.Navigator>
</NavigationContainer>
);
};
export default App;

Button with Icon component

I have a <Button /> component and an <Icon/> component.
I try to implement a button with an icon.
The Button.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Button from "../components/Button";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Button/Primary", module)
.add("With icon", () => (
<Button><Icon type={iconTypes.arrowRight}/></Button>
))
That works fine but I would like the api for a Button with an icon to be-
<Button icon={icons.arrow}>Click Me</Button>
How can I achieve that?
The Icon.jsx story:
import React from "react";
import { storiesOf } from "#storybook/react";
import Icon from "../components/Icon/Index";
import { iconTypes } from "../components/Icon/Index";
storiesOf("Icon", module)
.add("Arrow Right", () => (
<Icon type={iconTypes.arrowRight}>
</Icon>
))
.add("Arrow Left", () => (
<Icon type={iconTypes.arrowLeft}>
</Icon>
));
The <Button /> component:
import React from 'react';
import { css, cx } from 'emotion';
import colors from '../styles/colors';
export default function Button({
children,
...props
}) {
const mergedStyles = cx(
// css
);
// other css stuff
return (
<button {...props} disabled={disabled} className={mergedStyles}>
{children}
</button>
);
And <Icon /> component:
import React from "react";
import { css } from 'emotion';
import ArrowRight from "./arrow-right2.svg";
import ArrowLeft from "./arrow-left2.svg";
export const iconTypes = {
arrowRight: 'ARROW_RIGHT',
arrowLeft: 'ARROW_LEFT',
}
const iconSrc = {
ARROW_RIGHT: ArrowRight,
ARROW_LEFT: ArrowLeft,
}
const circleStyles = css({
width: 24,
height: 24,
borderRadius: "50%",
backgroundColor: "#f7f7f9",
display: "flex",
justifyContent: "center"
});
export default function Icon({ type }) {
return (
<div className={circleStyles}>
<img src={iconSrc[type]} />
</div>
)
};
Any help would be appreciated.
import React from 'react';
import {css, cx} from 'emotion';
import colors from '../styles/colors';
//import your ICON component & make sure your path is right
import Icon from "./../Icon";
export default function Button({
children,
disabled,
icon,
...props
}) {
const mergedStyles = cx(//your css);
return (
<button {...props} disabled={disabled} className={mergedStyles}>
// If icon prop is provided then render ICON component
{icon && <Icon type={icon}/>}
//Other children
{children}
</button>
);
}
in render of Button, you can do something like that :
Button.js:
render(){
const { icon } = this.props
return(
<Button>
{icon && <Icon type={icon}/>}
<Button>
)
}

implement BackButton in react-admin

I need to implement a <BackButton /> in react-admin for example when I open show page for a resource I need able to back to the list page.
Can you guide me to implement this?
I'm not familiar with react-admin routing mechanism.
Now I'm using this component in my edit form actions props:
const MyActions = ({ basePath, data, resource }) => (
<CardActions>
<ShowButton basePath={basePath} record={data} />
<CloneButton basePath={basePath} record={data} />
{/* Need <BackButton /> here */}
</CardActions>
);
export const BookEdit = (props) => (
<Edit actions={<MyActions />} {...props}>
<SimpleForm>
...
</SimpleForm>
</Edit>
);
You can use react-router-redux's goBack() function to achieve this.
For example, your button component will look something like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '#material-ui/core/Button';
import { goBack } from 'react-router-redux';
class BackButton extends Component {
handleClick = () => {
this.props.goBack();
};
render() {
return <Button variant="contained" color="primary" onClick={this.handleClick}>Go Back</Button>;
}
}
export default connect(null, {
goBack,
})(BackButton);
Now use that button component in your CardActions.
You can get help from an example which uses react-router-redux's push() function in a similar way from the official docs.
Create a back button. This one will pass props and children (text) and uses react-router directly, which I think makes more sense and keeps your code simple.
// BackButton.js
import React from 'react'
import Button from '#material-ui/core/Button'
import { withRouter } from 'react-router'
const BackButton = ({ history: { goBack }, children, ...props }) => (
<Button {...props} onClick={goBack}>
{children}
</Button>
)
export default withRouter(BackButton)
Example usage:
import { Toolbar, SaveButton } from 'react-admin'
import BackButton from './BackButton'
const SomeToolbar = props => (
<Toolbar {...props}>
<SaveButton />
<BackButton
variant='outlined'
color='secondary'
style={{ marginLeft: '1rem' }}
>
Cancel
</BackButton>
</Toolbar>
)
The complete code is below.
//BackButton.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import compose from 'recompose/compose';
import { withStyles, createStyles } from '#material-ui/core/styles';
import { translate } from 'ra-core';
import Button from '#material-ui/core/Button';
import ArrowBack from '#material-ui/icons/ArrowBack';
import classnames from 'classnames';
import { fade } from '#material-ui/core/styles/colorManipulator';
const styles = theme =>
createStyles({
deleteButton: {
color: theme.palette.error.main,
'&:hover': {
backgroundColor: fade(theme.palette.error.main, 0.12),
// Reset on mouse devices
'#media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
});
const sanitizeRestProps = ({
basePath,
className,
classes,
label,
invalid,
variant,
translate,
handleSubmit,
handleSubmitWithRedirect,
submitOnEnter,
record,
redirect,
resource,
locale,
...rest
}) => rest;
class BackButton extends Component {
static contextTypes = {
router: () => true, // replace with PropTypes.object if you use them
}
static propTypes = {
label: PropTypes.string,
refreshView: PropTypes.func.isRequired,
icon: PropTypes.element,
};
static defaultProps = {
label: 'ra.action.back',
icon: <ArrowBack />,
};
render() {
const {
className,
classes = {},
invalid,
label = 'ra.action.back',
pristine,
redirect,
saving,
submitOnEnter,
translate,
icon,
onClick,
...rest
} = this.props;
return (
<Button
onClick={this.context.router.history.goBack}
label={label}
className={classnames(
'ra-back-button',
classes.backButton,
className
)}
key="button"
{...sanitizeRestProps(rest)}>
{icon} {label && translate(label, { _: label })}
</Button>
)
}
}
const enhance = compose(
withStyles(styles),
translate
);
export default enhance(BackButton);
//Toolbar.js
import React from 'react';
import {
Toolbar,
SaveButton,
DeleteButton,
} from 'react-admin';
import { withStyles } from '#material-ui/core';
import BackButton from './BackButton'
const toolbarStyles = {
toolbar: {
display: 'flex',
justifyContent: 'space-between',
},
};
export const CustomEditToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<DeleteButton/>
<BackButton/>
</Toolbar>
));
export const CustomCreateToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<BackButton/>
</Toolbar>
));

Resources