One form, but different state - react-native

I'm moving now from other technologies to React Native and I have a problem. I have one presentational component which is <TaskInput />.
const TaskInput = (props: ITaskInputProps) => {
return (
<View style={styles.container} >
<Text style={styles.title}>{props.title}</Text>
<TextInput
style={styles.input}
multiline
scrollEnabled={false}
/>
</View>
)
}
ParentComponent over TaskInput
import React from 'react';
import { View } from 'react-native';
import styles from './styles';
import TaskInputContainer from '../task-input';
interface ITaskConfigurationProps {
title: string,
isInputForm?: boolean,
isRequired?: boolean,
}
const TaskConfiguration = (props: ITaskConfigurationProps) => {
return (
<View style={(props.isRequired) ? [styles.container, {backgroundColor: '#f25e5e'}] : styles.container}>
{ props.isInputForm && <TaskInputContainer title={props.title} /> }
</View>
);
}
export default TaskConfiguration;
const TaskScreen = (props: ITaskScreenProps) => {
return (
<View style={styles.container}>
<SectionTitle title={'Task Settings'} />
<ScrollView contentContainerStyle={styles.configurations}>
<TaskConfiguration title={"What you need to do?"} isInputForm={true} isRequired={true} />
<TaskConfiguration title={"Description"} isInputForm={true} />
<TaskConfiguration title={"Deadline"} />
<TaskConfiguration title={"Priority"} />
</ScrollView>
<Button isDone={true} navigation={props.navigation} />
</View>
)
}
TaskInput component takes one prop which is title and it will be in two places on my screen. One component will be called "Enter main task", another one is "Description". But this component will accept different states like currentMainTextInput and currentDescriptionTextInput. This is my idea of re-usable component TextInput, but I can't do what I want because if I set type in one input - other input will re-render with first input (both of them are one presentational component).
I want to use this dumb component in any place of my app. I don't want to create a new identical component and duplicate code, How can I do that? (P.S. I was thinking about "redux or class/hooks", but what should I use...)

Related

How to implement onPress in menu.item? native-base#3.2.1-rc.1

For some reason there's no nativeBase api or documentation on this. I can not get a menu.item to respond to press/click no matter what I try.
Attempts
putting Text/Button elements from react-native into the menu. item
putting the menu in multiple different screens
creating a separate function instead of just arrow function
import React from "react"
import {
Menu,
HamburgerIcon,
Box,
Center,
NativeBaseProvider,
usePropsResolution,
Text,
Pressable
} from "native-base"
import {Alert, } from "react-native";
import { logout } from "../_redux/_actions/authentication.actions";
export const NavMenu = (props) => {
const menuItems = ['Profile','Sign Out'];
return (
<Box h="80%" w="95%" alignItems="flex-start">
<Menu w="150" top="-85" h="100%"
trigger={(triggerProps) => {
return (
<Pressable accessibilityLabel="More options menu" {...triggerProps}>
<HamburgerIcon color="black" />
</Pressable>
)
}}
>
**<Menu.Item onPress={()=>alert("Alert Title")}>**
Logout
</Menu.Item>
</Menu>
</Box>
)
}
export default () => {
return (
<NativeBaseProvider>
<Center flex={1} px="1">
<NavMenu />
</Center>
</NativeBaseProvider>
)
}
I found workaround around this problem. Would be interested in better solution, but this should work (it is also not very clean solution):
<Menu.Item onPress={()=>alert("Alert Title")}>
<Pressable onPress={()=>alert("Alert Title")}><Text>Logout</Text></Pressable>
</Menu.Item>
Basically what I am doing here is putting onPress to the Menu.Item and then same onPress to the child. You can use this approach for more complicated situations like adding button to the menu like this:
<Menu.Group title="Účet">
<Menu.Item onPress={() => console.log('log')}>
<Button colorScheme="red" onPress={() => console.log('log')} leftIcon={<Icon size="s" as={<MaterialIcons name='logout' />} />}>
Logout
</Button>
</Menu.Item>
</Menu.Group>

How to pass state into Parent?

I am fairly new to React, and I am making a small single page application, and I am trying to figure out how to pass the employee ID to the parent.
So there is an issue here, which I already know.... but trying to figure out a solution.
There will be 3 screens first starts with the ID.
When I have it as the code listed below, everytime I type, the state rerenders, however it DOES work. It just really annoying to type.
import React, { useState } from 'react';
import {
Container,
Header,
Title,
Body,
Content,
Item,
Input,
Icon,
Text,
View,
} from 'native-base';
export default function App() {
const [id, setId] = useState('');
const IdScreen = () => {
return (
<View>
<Text>Enter your Employee ID</Text>
<Item rounded style={styles.textBox}>
<Icon type='Ionicons' name='person' style={{ fontSize: 30 }} />
<Input
onChangeText={(text) => {
setId(text);
}}
value={id}
/>
</Item>
</View>
);
};
return (
<Container>
<Header>
<Body>
<Title>Employee Enroll</Title>
</Body>
</Header>
<Content>
<IdScreen />
</Content>
</Container>
);
}
If I make this change... the typing part is fine, but when I go to push this to the API, it wont be readable.
const IdScreen = () => {
const [id, setId] = useState('');
return (
<View>
<Text>Enter your Employee ID</Text>
<Item rounded style={styles.textBox}>
<Icon type='Ionicons' name='person' style={{ fontSize: 30 }} />
<Input
onChangeText={(text) => {
setId(text);
}}
value={id}
/>
</Item>
</View>
);
};
I am looking for a way to type in the person ID, then save that state to the main function so that I can use that variable later.
I just don't know how to do that.
First of all take the IdScreen component out of the App component (You should have your components in different files), then if you want to pass the id from IdScreen to App you need to lift up the state. In order to do that you will need to the following:
Put this const [id, setId] = useState(''); only in the App component, then pass the setId function to the IdScreen component as a props, like this:
<IdScreen id={id} setId={setId} />
Then in your IdScreen component you can use the setter function and the value as props, like this:
props.id and props.setId

Entering input causes modal reloading automatically

In my React Native 0.62.3 app, a modal is used to collect user input. Here is the view code:
import { Modal, View, TextInput, Button } from 'react-native';
const [price, setPrice] = useState(0);
const [shippingCost, setShippingCost] = useState(0);
const ReturnModal = () => {
if (isModalVisible) {
return (
<View style={styles.modalContainer}>
<Modal visible={isModalVisible}
animationType = {"slide"}
transparent={false}
onBackdropPress={() => setModalVisible(false)}>
<View style={styles.modal}>
<Text>Enter Price</Text>
<TextInput keyboardType={'number-pad'} onChange={priceChange} value={price} autoFocus={true} placeholder={'Price'} />
<TextInput keyboardType={'number-pad'} onChange={shChange} value={shippingCost} placeholder={'SnH'} />
<View style={{flexDirection:"row"}}>
<Button title="Cancel" style={{bordered:true, backgroundColor:'red'}} onPress={modalCancel} />
<Button title="OK" style={{bordered:true, backgroundColor:'white'}} onPress={modalOk} />
</View>
</View>
</Modal>
</View>
)
} else {
return (null);
}
}
return (
<Container style={styles.container}>
//.....view code
<ReturnModal />
</Container>
)
Here is 2 functions to reset state of price and shippingCost:
const priceChange = (value) => {
if (parseFloat(value)>0) {
setPrice(Math.ceil(parseFloat(value)));
}
};
const shChange = (value) => {
if (parseFloat(value)>=0) {
setShippingCost(Math.ceil(parseFloat(value)));
}
};
The problem is that whenever entering in the price field with keystroke, the modal reloads/resets itself automatically. Tried onChangeText in TextInput and it has the same problem.
It seems like you're declaring your hooks outside your component. Try putting them inside your ReturnModal function instead, like this:
const ReturnModal = () => {
const [price, setPrice] = useState(0);
const [shippingCost, setShippingCost] = useState(0);
...
Documentation reference: Using the State Hook.
Also, I would strongly recommend using the React Hooks ESLint Plugin (among others) to detect issues with your hooks. Here is a guide on how to add this to your React Native project: Add Eslint Support to your React Native Project + React Hooks Rules.
Instead of using animationType = {"slide"} try using animatonType : 'none'

React native changing button color not working properly

I have my reusable component for Button :
import React, { Component } from "react";
import { View, Text, TouchableOpacity } from "react-native";
import { styles } from "./styles";
class TransactionFees extends Component {
state = {
pressed: false,
};
render() {
const { pressed } = this.state;
const { speed, eth, usd } = this.props;
return (
<View>
<TouchableOpacity style={ pressed ? styles.pressedButton : null } onPress={() => this.setState({ pressed: !pressed })}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
</View>
);
}
}
export default TransactionFees;
This is how I use it in another component :
<View style={styles.transactionFeeChoices}>
<TransactionFees speed={"Slow"} eth={"0.000010"} usd={"0.02"} />
<TransactionFees speed={"Average"} eth={"0.000030"} usd={"0.03"} />
<TransactionFees speed={"Fast"} eth={"0.000050"} usd={"0.04"} />
</View>
When i press the button the background color changes to blue and the problem is When I click on second button,the first button is not going to default background color
Any solutions on how to solve this please?
The way your TransactionFees component is currently created means that each TransactionFees occurrence has its own internal state. So when one TransactionFees changes, that doesn't mean the others are automatically updated. You can see them as autonomous components.
In your case however, the TransactionFees occurrences shouldn't be automomous, a change in one occurence should reflect in the others. This is a classic react pattern where the most common solution is to "lift the state up" from the TransactionFeeds component into the parent. There even is a react article about in the official docs
Steps to take
have one state variable in the parent that keeps the selected TransactionFees
pass in pressed as a prop to the TransactionFees occurrences
pass in a change handler function that the TransactionFees component can call when one of the buttons is pressed
In code, this will be more or less:
class Manager extends Component {
constructor(props) {
super(props);
this.state = {
selected: '',
};
}
onChangeSelection = selected => {
this.setState({
selected,
});
}
render() {
const { selected } = this.state;
return (
<View style={styles.transactionFeeChoices}>
<TransactionFees speed={"Slow"} eth={"0.000010"} usd={"0.02"} pressed={selected === 'Slow'} onPress={() => this.onChangeSelection('Slow')} />
<TransactionFees speed={"Average"} eth={"0.000030"} usd={"0.03"} pressed={selected === 'Average'} onPress={() => this.onChangeSelection('Average')} />
<TransactionFees speed={"Fast"} eth={"0.000050"} usd={"0.04"} pressed={selected === 'Fast'} onPress={() => this.onChangeSelection('Fast')} />
</View>
);
}
}
class TransactionFees extends Component {
render() {
const { speed, eth, usd, pressed, onPress } = this.props;
return (
<View>
<TouchableOpacity style={ pressed ? styles.pressedButton : null } onPress={onPress}>
<Text style={styles.transactionFeeTitle}>{speed}</Text>
<Text style={styles.transactionFeeSubText}>{eth} ETH</Text>
<Text style={styles.transactionFeeSubText}>$ {usd} </Text>
</TouchableOpacity>
</View>
);
}
}
You have created 3 instances of TransactionFees and they will have their own separate state.
That's why click on the second button does not change the state of the first button.
If you want to make either of button-click trigger that style change, you need to use shared value between all those TransactionFees instances.
There can be 2 ways to do this depending on where to store that value
You can store it as a state of the parent component and pass it down to TransactionFees component
You can store it in redux store and use it inside TransactionFees by connecting the component to the store.
To do so
You need to keep the stat in the parent
And pass to each button the function to change the state
And move the current stat to each button
like this
<TransactionFees speed={"Slow"}
setClicked(()->{
this.setState({clicked:true})
clicked={this.state.clicked}
eth={"0.000010"} usd={"0.02"}
/>

How to pass a already implemented Component inside the Copilot Step in react-native-copilot

I am trying to built a guided tour for my app using react native copilot
https://github.com/okgrow/react-native-copilot
and i cannot figure out how to use a already built component inside the its CopilotSteps as mentioned in the tutorial.
This is the my code so far and it gives me the following error
<CopilotStep
text="This is a hello world example!"
order={1}
name="hello"
>
{({ copilot }) => (
<Card snow to={`${basePath}/account`} {...copilot}>
<Row inline justify="center" fluid>
<Block inline justify="center">
<FIcon name="shield" size={25} />
</Block>
<Block justify="center">
<P compact>Account and Security</P>
<P compact small helper>
Edit Your Account Information
</P>
</Block>
<Block inline justify="center">
<FIcon name="chevron-right" size={25} />
</Block>
</Row>
</Card>
)}
</CopilotStep>
error =>
D:\My Work\Company Projects\Ongoing\ZappyFoods\Mobile App\zappyfood_app\node_modules\react-native\Libraries\Core\ExceptionsManager.js:63 Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
what should i do to get this code run proerly
You have to make the component "walkthroughable", so for example if my original component was a simple TouchableOpacity button.
<TouchableOpacity
onPress={this.callFunction}
>
<Icon name="photo" type="FontAwesome" />
</TouchableOpacity>
Then to make it work with copilot first import walkthroughable.
import {
copilot,
walkthroughable,
CopilotStep
} from "#okgrow/react-native-copilot";
Then call the walkthroughable function passing our TouchableOpacity component.
const WalkthroughableTouchableOpacity = walkthroughable(TouchableOpacity);
Finally add the copilot step and use the new Walkthroughable component in place where you would've used TouchableOpacity.
<CopilotStep
text="Hey! This is the first step of the tour!"
order={1}
name="openApp"
>
<WalkthroughableTouchableOpacity
onPress={this.callFunction}
>
<Icon name="photo" type="FontAwesome" />
</WalkthroughableTouchableOpacity>
</CopilotStep>
So the entire file could look like
import {
copilot,
walkthroughable,
CopilotStep
} from "#okgrow/react-native-copilot";
import { Icon } from "native-base";
import React, { Component } from "react";
import { TouchableOpacity } from "react-native";
const WalkthroughableTouchableOpacity = walkthroughable(TouchableOpacity);
class Example extends Component {
componentDidMount = async () => {
this.props.copilotEvents.on("stepChange", () => {});
this.props.start();
};
callFunction = () => {
console.log("Button Pressed.");
};
render() {
return (
<CopilotStep
text="Hey! This is the first step of the tour!"
order={1}
name="openApp"
>
<WalkthroughableTouchableOpacity onPress={this.callFunction}>
<Icon name="photo" type="FontAwesome" />
</WalkthroughableTouchableOpacity>
</CopilotStep>
);
}
}
export default copilot({
animated: true,
overlay: "svg"
})(Example);