Send data back from component in react native - 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.

Related

Get user input from input field in react similar to getElementById in react native using props

I am doing a loan calculation app and i run into the trouble since i am new to react native and previously i have been manipulating the DOM using querySelector or getElementById functions. However this does not work in react, and i am using state to store the value from the user, but i just can't seem to get it right, What am i doing wrong?
I've inserted the calculation element that is later rendered in app.js. All elements are showing up with no error, but the problem is to get user input data and then be able to use that data and do calculations.
Here is my Class
class LanKalkylElement extends React.Component {
constructor(props) {
super(props);
this.state = {
loanAmount: 20000,
loanInterest: 2.5,
loanYear: 10,
};
}
changeAmount(loanAmount) {
this.setState(() => {
return {
loanAmount: parseFloat(loanAmount),
};
});
}
changeInterest(loanInterest) {
this.setState(() => {
return {
loanInterest: parseFloat(loanInterest),
};
});
}
changeYear(loanYear) {
this.setState(() => {
return {
loanYear: parseFloat(loanYear),
};
});
}
calcButton() {
Alert.alert(this.props.loanAmount);
}
buttonHomeFunc() {
this.props.navigation.navigate('Start');
}
render() {
const {loanAmount, loanInterest, loanYear} = this.state;
return(
<View style={styles.contentStyle}>
<Text style={styles.text}> Lånebelopp </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanAmount}
onValueChange={this.changeAmount.bind(this)} />
<Text style={styles.text}> Ränta </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanInterest}
onValueChange={this.changeInterest.bind(this)} />
<Text style={styles.text}> Antal år: {String(loanYear)}</Text>
<Slider step={1}
maximumValue={15}
value={loanYear}
onValueChange={this.changeYear.bind(this)} />
<Button title='Kalkylera' onPress={() => this.calcButton()}/>
<Text style={styles.textResult}>Total summa att återbetala:</Text>
<Text style={styles.textResult}>varav räntekostnad:</Text>
<Button title='Tillbaka' onPress={() => this.buttonHomeFunc()}/>
</View>
)
}
}
export default withNavigation(LanKalkylElement);
When a user changes a value in a text input, onValueChange is called. You have bound this prop to functions that modify the state for this component.
This means the value in the text input will always match the value in the state. Therefore, if you need to access the value in a text input you would simply retrieve it from the state, like this:
const loanAmount = this.state.loanAmount;
doSomethingWithLoanAmount(loanAmount);

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

Best method to optimize performance of FlatList Items

This a simple FlatList:
class Products ..
render() {
return (
<FlatList
renderItem={this._renderItem}
);
}
I want to create a list of items and navigate to Detail Page by onPress items.
Can Please tell me which method is better?
Method 1:
Insert navigate to Detail page in child component(CardProduct component) like this:
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
/>
);
and in CardProduct component:
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={() => this.props.navigation.navigate('Details',{productId:id})}>
...
);
}
Method 2:
Insert navigate to Detail page in current component(Products component) like this:
_onPressItem = (id: string) => {
this.props.navigation.navigate('Details',{productId:id});
};
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
onPressItem={this._onPressItem}
/>
);
and in CardProduct component:
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={this._onPress}>
...
);
}
I used to do the method 1, but I read this guide.
Short answer:
You should go for method2.
Explanation:
In method1 you are using an arrow function in CardItem's onPress, so everytime CardProduct is re-rendered a new reference of onPress is created, which forces CardItem to re-render, even if all the other props are staying the same. In method 2 you are binding the function to context, which won't force a re-rendering of the CardItem.
By the way, in general it is a good idea to prevent the usage of arrow functions in render().
One step for performance optimization in react-native flatlist, is using a stateless functional component for the renderItem. and you should always give each item a unique key.

Pass parameters with props in react native

I want to pass the clicked searchItem's id from my component. My code is as follows.
const searchResultList = props => (
<View style={styles.container}>
<ScrollView keyboardShouldPersistTaps="always">
{props.itemList.map(item => (
<SearchResultListItem
key={item.id}
imageSource={item.workoutImage}
mainText={item.workoutName}
subText={item.length + " | " + item.difficulty}
onItemPressed={props.onItemPressed}
/>
))}
</ScrollView>
</View>
);
When, onItemPressed, i want to pass the item.id with the prop.onItemPressed. This is my screen.
<SearchResultList
itemList={this.props.searchedWorkouts}
onItemPressed={id => alert(item.id)} //this.onLoadWorkoutDetailView()}
/>
How can I achieve this? I want the clicked itemId to my main screen.
You need to make an addition function. Straightforward it will be like this:
...
onItemPressed={() => props.onItemPressed(item.id)}
...
But, it`s not a good solution from performance point of view.
The better way is to make a method and bind all items to this method.
class SearchResultList extends React.PureComponent {
constructor(props) {
super(props);
const {itemsList} = props;
this._onPressHandlers = {};
for (let {id} for itemsList) {
this._onPressHandlers[id] = this._onItemPress.bind(this, id);
}
}
_onItemPress(id) {
return this.props.onItemPressed(id);
}
render() {
return (
<View style={styles.container}>
<ScrollView keyboardShouldPersistTaps="always">
{props.itemList.map(item => (
<SearchResultListItem
key={item.id}
imageSource={item.workoutImage}
mainText={item.workoutName}
subText={item.length + " | " + item.difficulty}
onItemPressed={this._onPressHandlers[item.id]}
/>
))}
</ScrollView>
</View>
);
}
}
In this case the handlers will not be generated on each render of list and will not cause re-render of SearchResultListItem component.
Obviously, if you expect changes of the itemsList prop, you will need to rebind it on componendWillReceiveProps method.

React Native list view data parsing from JSON

This is the JSON im trying to parse and show in my list view.
The data I would like to show on my list view is ZoneInfo["Name"] as a section header. For the list view, there would be 3 text showing the Name,QueueTime or ShowTime.
I have my JSON saved in my state variable.
This is the code I've been trying to retrieve the data from the JSON.
{this.state.loading? <Spinner /> : <List dataArray={this.state.results.items} renderRow={(item) =>
<ListItem>
<Text>{item}</Text>
</ListItem>
} />}
Anyone can guide me on how I can parse the JSON and show it on my listview?
There are for sure multiple ways of achieving this, up till now I'm pretty happy using it this way:
I'm sure you will find your way around ;-)
export default class extends React.Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.renderSectionHeader = this.renderSectionHeader.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2});
this.state = {
dataSource: ds.cloneWithRowsAndSections({}),
};
}
componentDidMount(){
... load your json and assign data to the state
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(spots)
});
}
renderRow(rowData: string, sectionID: number, rowID: number) {
return (
<TouchableOpacity onPress={()=>this.onRowPress(rowData)}>
... your row content
</TouchableOpacity>
)
}
renderSectionHeader(sectionData, category) {
return (
<View style={styles.rowHeaderContainer}>
... your header content
</View>
)
}
render(){
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
enableEmptySections={true}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
}
}