How to pass state into Parent? - react-native

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

Related

Fail to prevent re-render by React.memo

I am building a react native app for which has a form page contains a lot of textInput and buttons. Each of fields do not causing performance issue.
However, when they are put together, the application will show js thread frame drops in the Perf Monitor. So I think the page is re-rendering all the components when any of the state is changed.
I tried 2 methods.
Moving components out of the main export function.
Using React.memo.
Unfortunately, both of them are not working. The app still lag and re-render every time for all components.
So I created a simple case for it. You may see the code below.
I would like to only re-render the ChildItem for only the respective state changed. For now, console will log from 1 to 5 for any button clicked. Since the props passed to memo component is not changed, I expect the console will only log 1 respective sentence for specific index when button is pressed.
import React, {useState, memo} from 'react';
import { View, Text, Button } from 'react-native';
const ChildeItem = memo( ({index, value, onPress}) =>{
console.log(`ChildeItem ${index} rendered`);
return(
<View style={{flexDirection: 'row'}}>
<Text style={{flex: 1, textAlign: 'center'}}>
{value}
</Text>
<View style={{flex: 1}}>
<Button
onPress={onPress}
title={"+"}
/>
</View>
</View>
)
});
export default function TestScreen({navigation}) {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const [value3, setValue3] = useState(0);
const [value4, setValue4] = useState(0);
const [value5, setValue5] = useState(0);
return(
<View>
<ChildeItem index={1} value={value1} onPress={()=>{setValue1(value1+1);}} />
<ChildeItem index={2} value={value2} onPress={()=>{setValue2(value2+1);}} />
<ChildeItem index={3} value={value3} onPress={()=>{setValue3(value3+1);}} />
<ChildeItem index={4} value={value4} onPress={()=>{setValue4(value4+1);}} />
<ChildeItem index={5} value={value5} onPress={()=>{setValue5(value5+1);}} />
</View>
)
}
Or I have any misunderstanding for React.memo? Any help will be appreciated. Thanks.
Your index and value props are ok for memoization, but your onPress prop causes re-render because on every render of the TestScreen, your onPress prop functions are re-created, so it's reference changes. You can prevent this by using useCallback hook.
export default function TestScreen({navigation}) {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const [value3, setValue3] = useState(0);
const [value4, setValue4] = useState(0);
const [value5, setValue5] = useState(0);
// Use useCallback for each function
const onPress1 = useCallback(() => setValue1(value1+1), [value1]);
const onPress2 = useCallback(() => setValue2(value2+1), [value2]);
const onPress3 = useCallback(() => setValue3(value3+1), [value3]);
const onPress4 = useCallback(() => setValue4(value4+1), [value4]);
const onPress5 = useCallback(() => setValue5(value5+1), [value5]);
return(
<View>
<ChildeItem index={1} value={value1} onPress={onPress1} />
<ChildeItem index={2} value={value2} onPress={onPress2} />
<ChildeItem index={3} value={value3} onPress={onPress3} />
<ChildeItem index={4} value={value4} onPress={onPress4} />
<ChildeItem index={5} value={value5} onPress={onPress5} />
</View>
)
}
By using useCallback hook, you're memoizing the onPress functions, so they are re-created only when the relevant value changes.

Passing state via route.params with React Navigation returning undefined

I'm attempting to pass a 'passcode' state as params in my React Native app.
I'm doing this by passing params into a 'navigation.navigate' call.
However, every time I navigate to the next screen, it's returning 'undefined' for 'route.params'.
For reference, here is my component I'm passing data FROM:
const SignUpPasscodeScreen = ({ navigation }) => {
const [passcode, setPasscode] = useState(0)
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Set passcode" />
<SubHeading content="You'll need this anytime you need to access your account." />
<Input inputText={ text => setPasscode(text) } inputValue={passcode} />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate({ routeName: 'SignUpLegalName', params: { passcode } } ) } />
</View>
</View>
)
}
And here's the component I'm passing data to, and where the error occurs upon navigation:
const SignUpLegalName = ({ route, navigation }) => {
const { passcode } = route.params
return (
<View>
<View style={styles.mainView}>
<SubLogo />
<Heading title="Tell us your name" />
<SubHeading content="This needs to be the same as what's on your passport, or any other form of recognised ID." />
<Input />
<Input />
</View>
<View style={styles.subView}>
<CtaButton text="Continue" onPressFunction={ () => navigation.navigate('SignUpLink')} />
</View>
</View>
)
}
I've tried two forms of passing the props through:
Passing it in as a second argument
Passing it in as a 'params' object as shown above
Both should work according to the documentation - link here
For reference, this is my route structure:
const switchNavigator = createSwitchNavigator({
loginFlow: createStackNavigator({
SignUpPasscode: SignUpPasscodeScreen,
SignUpLegalName: SignUpLegalName,
})
});
The above structure doesn't say to me that it's a nested structure which therefore requires any additional work to pass it through...
Can someone help? It'd be appreciated as it's giving me a headache!
Have a try with below code in the button press event:
<CtaButton
text="Continue"
onPressFunction={() => navigation.navigate('SignUpLegalName',{ passcode })}
/>

One form, but different state

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...)

React-native Input HOC for redux-form loses focus after type a symbol

I'm trying to use redux-form, but, as I read, I need HOC for Input field to replace onTextChange to onChange. I have:
import React from 'react';
import {Input} from 'native-base';
export default function InputField(props) {
const { input, ...inputProps } = props;
return (
<Input
{...inputProps}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
/>
);
};
and use it in my form:
<Item style={{marginTop: 10, width: "100%"}}>
<Field name="login" component={(props) => {
return (
<InputField {...props} keyboardType="email-address" placeholder='E-mail' />
)
}}/>
</Item>
But every time I type key, the field loses focus. Some "experts" recommend use focus() function. But what if I edit text in the middle of it?
Any solutions? Or maybe native-base have compatible textfield component?
It is strange, but it works ))
function InputFieldEmail(props) {
return <InputField {...props} keyboardType="email-address" placeholder='E-mail' />;
}
and use it instead arrow functions
<Field name="login" component={InputFieldEmail}/>
I think it's strange )
You must provide the InputField component as a prop so that its value is constant or put it outside the Item component, when it is inside another, every time the state of the upper level is updated, the lower level is forced to return. to start.
If your Input is inside an Item you are probably using a FlatList and your goal is to put it in the header.
You can try something like this:
<View style={{marginTop: 10, width: "100%"}}>
<Field name="login" component={(props) => {
return (
<InputField {...props} keyboardType="email-address" placeholder='E-mail' />
)
}}/>
</View>
<FlatList
ListHeaderComponent={() => {
<Item>
</Item>
}}
/>
Remember to put the styles that were in Item in View.

Send data back from component in react native

I want to filter my list of data but I want to keep my filter layout seperate to avoid having thousand lines of code... How can I send the filter options back to my main view?
I know I can add parameters to the <Filter /> component, but I need to find a way to send them back to the main screen..
Imagine my main.js screen looking like this:
<View>
<ListView dataSource={this.state.dataSource} renderRow={this.renderListItem}></ListView>
<Filter />
</View>
filter.js screen:
<View>
<Input>filter text here</Input>
</View>
Note: this is a minimized version
Taking the concepts from Thinking in React, this is what you should do:
class MyComponent extends Component {
state = {
filter1: '',
filter2: '',
// Other stuff
};
handleFilter1Change = (filterText) => {
// Update filter1 and dataSource
};
handleFilter2Change = (filterText) => {
// Update filter2 and dataSource
};
render() {
return (
<View>
<ListView dataSource={this.state.dataSource} renderRow={this.renderListItem}></ListView>
<Filter
filter1Value={this.state.filter1}
filter2Value={this.state.filter2}
onFilter1Change={this.handleFilter1Change}
onFilter2Change={this.handleFilter2Change} />
</View>
);
}
}
const Filter = ({ filter1, filter2, onFilter1Change, onFilter2Change }) => {
return (
<View>
<TextInput
value={filter1}
onChangeText={onFilter1Change} />
<TextInput
value={filter2}
onChangeText={onFilter2Change} />
</View>
);
}
Essentially, the rule of thumb is to send a parent function down, which the child will call, passing data to it.