react native ref not working in dynamic adding rows - react-native

I am creating spreadsheet in react native and i want to append columns if we click on add button, but when i used ref in dynamic cols it gives error. here is my code .. please tell me how i can use ref so it will not give me error. following is the error that i receive when i use ref ..
import React, { Component } from 'react';
import {
StyleSheet,
View,
TouchableOpacity,
Text,
TextInput,
ScrollView
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
var unique=1;
export default class Sheet extends Component {
constructor(props) {
super(props);
this.state = {
currentRowNo:1,
serialNo:1,
rows:this.props.species.length,
breaker:true,
appendRows:[],
index:1
}
}
rendercol(rowNo)
{
const col=[];
var serialNo=this.state.index;
col.push(<View key="rowNum" style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>{rowNo}</Text></View></View>);
for (var j=serialNo;j<=this.state.rows+serialNo-1;j++)
{
col.push(<TouchableOpacity style={styles.fixCellWidth} key={"key_"+j}><Text style={{fontSize:16}} onPress={this.changeValue.bind(this,j)} ref={"red"+j}>{console.log(this.props.action.bind(this,j))}</Text></TouchableOpacity>);
}
return <View key={"Row"+rowNo}>{col}</View>;
}
changeValue(val)
{
this.props.changeVal(val);
}
addRow(){
this.state.appendRows.push(
this.rendercol(this.state.index)
)
this.setState({
index: this.state.index + 1,
appendRows: this.state.appendRows
})
}
render()
{
var _scrollView: ScrollView;
const row=[];
const sheet=[];
sheet.push(<View key="headRow" style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>S./#</Text></View></View>);
this.props.species.map((option) => {
sheet.push(<View key={option.code} style={styles.column}><View style={[styles.fixCellWidth,{backgroundColor:"#f2f2f2"}]}><Text style={{fontSize:20}}>{option.code}</Text></View></View>);
});
sheet.push(<TouchableOpacity key="rowAdd" style={styles.column} onPress={this.addRow.bind(this)}><View style={[styles.fixCellWidth,{backgroundColor:"green"}]}><Icon name="plus" style={[styles.icon,styles.fontWhite]}/></View></TouchableOpacity>);
return (
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }}
automaticallyAdjustContentInsets={false}
horizontal={false}
style={{height:650,paddingBottom:10}}>
<View style={styles.column}>
<View>{sheet}</View>
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }}
automaticallyAdjustContentInsets={false}
horizontal={true}
style={[styles.scrollFull]}>
{this.state.appendRows}
</ScrollView>
</View>
</ScrollView>
);
}
}

You are repeating ref assignation in your render function:
return (
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }} // <- here
automaticallyAdjustContentInsets={false}
horizontal={false}
style={{height:650,paddingBottom:10}}>
<View style={styles.column}>
<View>{sheet}</View>
<ScrollView
ref={(scrollView) => { _scrollView = scrollView; }} // <- here
automaticallyAdjustContentInsets={false}
horizontal={true}
style={[styles.scrollFull]}>
{this.state.appendRows}
</ScrollView>
</View>
</ScrollView>
);
But that is not the problem. The problem is that you are creating refs outside render method of the component, to be specific in your rendercol function. Here is the error explanation: https://gist.github.com/jimfb/4faa6cbfb1ef476bd105.
You can add rows using state, look at this approach: Append TextInput component one after another when press button in React Native

import React, { Component } from 'react';
import { View, Text, Platform, StyleSheet, TouchableOpacity,TouchableHighlight, Animated, ScrollView, Image } from 'react-native';
let index = 0
export default class App extends Component {
constructor(context) {
super(context);
this.state = {
rows: []
}
}
_addRow(){
this.state.rows.push(index++)
this.setState({ rows: this.state.rows })
}
render() {
let CheckIndex = i => {
if((i % 2) == 0) {
return styles.gray
}
}
let rows = this.state.rows.map((r, i) => {
return <View key={ i } style={[styles.row, CheckIndex(i)]}>
<Text >Row { r }, Index { i }</Text>
</View>
})
return (
<View style={styles.container}>
<TouchableHighlight onPress={ () => this._addRow() } style={styles.button}>
<Text>Add new row</Text>
</TouchableHighlight>
{ rows }
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 60,
},
gray: {
backgroundColor: '#efefef'
},
row: {
height:40,
alignItems: 'center',
justifyContent: 'center',
borderBottomColor: '#ededed',
borderBottomWidth: 1
},
button: {
alignItems: 'center',
justifyContent: 'center',
height:55,
backgroundColor: '#ededed',
marginBottom:10
}
});

Related

How to re-render after state change in React-Native?

I am developing React Native app, where must be overlay view before starting. Like "welcome screen". After you have read content in overlay view, you press ok.
setState is not working. Throw error TypeError: _this.setState is not a function
I'll tried to use setState like this:
removeElement = () => {
//this.state.loadingScreen = false;
this.setState({
loadingScreen: false
})
console.log(`Staten arvo nyt: ` + this.state.loadingScreen);
When I use setState it should re-render component, but I don't know what is the reason why it not works. I can change state this.state.loadingScreen = false;, but it is not re-rendering. And forceUpdate() not working either.
QUESTION: How can I render component again to get rid off overlay view?
My code:
import React, { useState, setState, Component } from "react";
import { Text, View, StyleSheet, TextInput, Button, Alert, TouchableOpacity} from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
state = {
loadingScreen: true,
}
let userInput = "";
let userInputName = "";
readUserInput = (text) => {
userInput = text
}
readUserInputName = (text) => {
userInputName = text
}
checkUserInput = () => {
if(userInput.length < 1 && userInputName.length < 1){
Alert.alert("Tarkista rekisterinumero ja nimi")
}
else{
storeData = async () => {
try{
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
}
catch(e){
console.log(e);
}
}
storeData();
navigation.navigate("GeneralInspection")
}
}
renderElement = () =>{
if(this.state.loadingScreen == true)
return <View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{fontSize:20}}>Change state to false</Text>
</TouchableOpacity>
</View>;;
return null;
}
removeElement = () => {
this.state.loadingScreen = false
}
setTimeout(
function() {
}
.bind(this),
1000
);
return (
<View style={styles.view}>
{ this.renderElement() }
<Text style={styles.text}>Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{fontSize:20}}>Aloita</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
view: {
height: "100%",
width: "100%",
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 40,
fontWeight: "bold",
padding:10,
fontStyle:"italic"
},
inputStyle: {
height:50,
borderColor:"black",
borderWidth:1,
width:"50%",
marginBottom:15,
textAlign:"center",
fontSize: 20,
borderRadius:5,
},
button: {
backgroundColor:"#007bff",
borderRadius:5,
padding:8,
width:"50%",
height: 60,
alignItems:"center",
justifyContent: "center",
},
fullScreen: {
height: "100%",
width: "100%",
zIndex: 100,
position: "absolute",
backgroundColor: "red",
}
});
You are using a functional component in the OP, which requires you to use useState Hooks for managing your state.
If you were using a class component, you could use the setState approach. In both cases, React will take care of the re-rendering part.
If using class-based approach, mutating state like this.state.loadingScreen = false will not trigger a re-render of the UI. You will have to use the setState method instead.
Class-based approach
import React from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default class StartPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loadingScreen: true,
userInput: "",
userInputName: ""
};
}
readUserInput = (text) => {
this.setState({
userInput: text
});
}
readUserInputName = (text) => {
this.setState({
userInputName: text
});
}
storeData = async () => {
const { userInput, userInputName } = this.state;
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
this.props.navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
}
checkUserInput = () => {
const { userInput, userInputName } = this.state;
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
this.storeData();
}
renderElement = () => {
if (this.state.loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
}
removeElement = () => {
this.setState({
loadingScreen: false
});
}
render() {
return (
<View style={styles.view} >
{ this.renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={this.state.userInputName}
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={this.state.userInput}
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
}
Functional component-based approach
import React, { useState } from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
const [loadingScreen, setLoadingScreen] = useState(true);
const [userInput, setUserInput] = useState('');
const [userInputName, setUserInputName] = useState('');
const readUserInput = (text) => {
setUserInput(text);
};
const readUserInputName = (text) => {
setUserInputName(text);
};
const storeData = async () => {
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
};
const checkUserInput = () => {
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
storeData();
};
const renderElement = () => {
if (loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
};
const removeElement = () => {
setLoadingScreen(false);
};
return (
<View style={styles.view} >
{renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={userInputName}
onChangeText={readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={userInput}
onChangeText={readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}

Flatlist data not showing up on screen

Trying to make a simple to-do list. My AddTodo component works fine and I don't believe it is causing the issue but my Flatlist does not show the data. I have no idea why as there are no errors. The issue appears with or without the scroll view.
I've tried messing around with the width and height of the items and the list itself but nothing seems to do the trick.
my mainTodo file:
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView } from 'react-native';
import AddTodo from './AddTodo';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo() {
let todos = this.state.todos;
todos.unshift({
id: todos.length + 1,
todo: this.state.textInput,
completed: false
});
this.setState({
todos,
textInput: ''
});
}
render() {
return (
<View style={{ flex: 1 }}>
<AddTodo
textChange={textInput => this.setState({ textInput })}
addNewTodo={() => this.addNewTodo()}
textInput={this.state.textInput}
/>
<ScrollView>
<FlatList
style={{ flex: 1 }}
data={this.state.todos}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</ScrollView>
</View>
);
}
}
export default MainTodo;
my TodoItem file:
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
todoItem: {
width: 40,
height: 40,
borderBottomColor: '#DDD',
borderBottomWidth: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 15
}
});
export default TodoItem;
Under my addtodo component nothing shows up, it's just a blank screen.
In the maintodo file you are rendering the AddTodo component but i didn't see your AddTodo component. So you can update your code accordingly.
In the TodoItem remove the style applied to TouchableOpacity so that your code looks like
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
});
export default TodoItem;
And in the MainTodo
update your addNewTodo function as
addNewTodo = () => {
const todo = {
id: this.state.todos.length,
title: this.state.textInput,
completed: false
}
this.setState({todos: [...this.state.todos, todo ], textInput: ""})
}
create the TextInput and Button with parent View as flexDirection: "row" and so when TextInput is changed it's value is set in the textInput and when Button is pressed it will create new object and add it to the todos and set the value of TextInput to empty.
and final code can be as
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView, TextInput, Button } from 'react-native';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo = () => {
const todo = {
id: this.state.todos.length,
title: this.state.textInput,
completed: false
}
this.setState({todos: [...this.state.todos, todo ], textInput: ""})
}
render() {
return (
<View style={{ flex: 1, marginTop: 30, paddingHorizontal: 20 }}>
<View style={{flexDirection: "row", alignItems: "center", justifyContent: "space-between"}}>
<TextInput style={{borderWidth: 1, borderColor: "black"}} onChangeText={textInput => this.setState({textInput})} placeholder="Enter todo text" value={this.state.textInput} />
<Button onPress={this.addNewTodo} title="Add todo" />
</View>
<FlatList
contentContainerStyle={{flexGrow: 1}}
data={this.state.todos}
extraData={this.state.todos}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</View>
);
}
}
export default MainTodo;
use the code
mainTodo file:
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, ScrollView } from 'react-native';
import AddTodo from './AddTodo';
import TodoItem from './TodoItem';
class MainTodo extends Component {
constructor() {
super();
this.state = {
textInput: '',
todos: [
{ id: 0, title: 'walk rocky', completed: false },
{ id: 1, title: 'pickup dinner', completed: false }
]
};
}
addNewTodo() {
let todos = this.state.todos;
todos.unshift({
id: todos.length + 1,
todo: this.state.textInput,
completed: false
});
this.setState({
todos,
textInput: ''
});
}
render() {
return (
<View style={{ flex: 1 }}>
<AddTodo
textChange={textInput => this.setState({ textInput })}
addNewTodo={() => this.addNewTodo()}
textInput={this.state.textInput}
/>
<ScrollView>
<FlatList
style={{ flex: 1 }}
data={this.state.todos}
extraData={this.state}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => {
return (
<TodoItem todoItem={item} />
);
}}
/>
</ScrollView>
</View>
);
}
}
export default MainTodo;
TodoItem file:
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
class TodoItem extends Component {
render() {
const todoItem = this.props.todoItem;
return (
<View>
<TouchableOpacity style={styles.todoItem}>
<Text style={(todoItem.completed) ? { color: '#aaaaaa' } : { color: '#313131' }}>
{todoItem.title}
</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
todoItem: {
width: 40,
height: 40,
borderBottomColor: '#DDD',
borderBottomWidth: 1,
backgroundColor:'red',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 15
}
});
export default TodoItem;

Using dynamic styles in react native

I've a couple of buttons in my react native app, on pressing any button, color should change. Code is as given below
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image,
Alert
} from 'react-native';
export default class Home extends Component {
constructor(props) {
super(props);
this.state= {
clicks: 0,
show: true,
isbuttonpress: false
};
}
onButtonState = () => {
this.setState({isbuttonpress: true});
}
render() {
return (
<View style={ [styles.container] }>
<Text style= { [styles.header] }>How likely is it that you would recommend this company to a friend or colleague?</Text>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>0</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>1</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>2</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>3</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>4</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.buttonContainer, this.state.isbuttonpress?styles.gobackred:styles.goback] } onPress = { () => this.onButtonState() }>
<Text style = { styles.gobacktext }>5</Text>
</TouchableHighlight>
<TouchableHighlight style = { [styles.nextContainer, styles.goback] } onPress = { () => this.onNextButtonState() }>
<Text style = { styles.gobacktext }>Next</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
fontSize: 20,
alignItems: 'center',
padding: 20,
},
container: {
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
height:45,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:45,
}, gobackred: {
backgroundColor: "#00a2b2",
},
goback: {
backgroundColor: "#00b5ec",
},
gobacktext: {
color: 'white',
},
nextContainer: {
height:45,
marginTop: 15,
justifyContent: 'center',
alignItems: 'center',
marginBottom:20,
width:250,
borderRadius:30,
}
})
Layout is as given below:
Whenever any option is selected, color of that button should change. But in my code, color of all buttons changes when clicked on any button. How can I fix it?
This happens because you used one state this.state.isbuttonpress for all button try to make the component of this code.
your TouchableHighlight Component
DemoButton.js
import React, { Component } from "react";
import { View, Text, TouchableHighlight } from "react-native";
export default class DemoButton extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
onButtonState = () => {
this.setState({ isbuttonpress: !this.state.isbuttonpress });
};
render() {
return (
<View>
<TouchableHighlight
style={{ backgroundColor: this.state.isbuttonpress ? "red" : "blue" }}
onPress={this.onButtonState}
>
<Text>{this.props.buttonTitle}</Text>
</TouchableHighlight>
</View>
);
}
}
App.js
import DemoButton from "./DemoButton";
export default class Heal extends Component {
constructor(props) {
super(props);
this.state = { isbuttonpress: false };
}
render() {
return (
<View>
<Header />
<DemoButton buttonTitle="0"/>
<DemoButton buttonTitle="1"/>
<DemoButton buttonTitle="2"/>
<DemoButton buttonTitle="3"/>
<DemoButton buttonTitle="4"/>
<DemoButton buttonTitle="5"/>
</View>
)
}
}

React Native - How to use a ref to set focus to an input when clicking on text

Here's what I'm doing:
export default class myComponent extends Component {
render() {
return (
<View>
<Text onPress={() => {this.input.focus()}}>Click Me</Text>
<Input ref={input => { this.input = input}}/>
</View>
)
}
}
I am using native-base components... not sure if that is affecting this.
When I press the Text component, I get an error stating: _this2.input.focus is not a function. (In '_this2.input.focus()', '_this2.input.focus' is undefined)
I don't use native-base, but at normal it should be like this:
import * as React from 'react';
import { Text, View, StyleSheet, TextInput, Dimensions } from 'react-native';
import { Constants } from 'expo';
const { width, height } = Dimensions.get('window');
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text onPress={() => {this.input.focus()}}>Click Me</Text>
<TextInput style={styles.input} ref={input => { this.input = input}}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
input: {
backgroundColor: '#bbb',
width: width - 40,
height: 40,
paddingHorizontal: 10,
paddingVertical: 5,
}
});
You can see the snacks here: https://snack.expo.io/#gasparteixeira/inputfocus
In case anyone comes across this stack overflow during the Hooks Era of React and React Native.
Solution with Hooks following the React Docs https://reactjs.org/docs/hooks-reference.html#useref
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
export default class myComponent extends Component {
public textInput: TextInput | null = null;
render() {
return (
<View>
<Text onPress={() => {this.textInput.focus()}}>Click Me</Text>
<TextInput ref={input => (this.textInput = input as any)}/>
</View>
);
}
}
export default class myComponent extends Component {
render() {
return (
<View>
<Text onPress={() => {this.input.focus()}}>Click Me</Text>
<Input ref={ref => (this.textinput= ref)}/>
</View>
)
}
}
and use this:
handleTextInput = () => {
this.textinput....
};

State does not change until the second button press? Adding a call back throws an error "invariant violation: invalid argument passed"

I'm trying to render a Flatlist that contains a store name. Upon clicking the store name, more information about the store should be displayed.
I attempted to change state and then use
{this.renderMoreDetails(item) && this.state.moreDetailsShown}
to get the additional information to appear. However, it was clear via console.log that state was only changed after a second button press.
From what I read in this article 1 and this article 2 it seemed as though I needed a callback function as an additional parameter to my setState().
I tried adding a callback function(probably incorrect, but I'm extremely lost at this point) and only got more errors.
I know that I have access to all of the data from renderItem inside FlatList. How do I make it appear upon click?
import React, { Component } from 'react';
import {
View,
FlatList,
Text,
TouchableWithoutFeedback,
ListView,
ScrollView
} from 'react-native'
import { NavigationActions } from 'react-navigation';
import { Header, Card, CardSection } from '../common';
import Config from '../Config';
export default class Costco extends Component<Props> {
constructor(props) {
super(props);
this.state = {
stores: [],
selectedItem: {id: null},
};
}
componentWillMount() {
const obj = Config.costcoThree;
const costcoArr = Object.values(obj);
this.setState({
stores: costcoArr,
})
}
renderListOfStores() {
return <FlatList
data={this.state.stores}
renderItem={ ({item}) => this.singleStore(item)}
keyExtractor={(item) => item.id}
extraData={this.state.selectedItem} />
}
singleStore(item) {
console.log(this.state.selectedItem.id)
return item.id === this.state.selectedItem.id ?
<TouchableWithoutFeedback
onPress={() => this.selectStore(item)}
>
<View>
<Text>{item.branchName}</Text>
<Text>Opening Time {item.openingTime}</Text>
<Text>Closing Time {item.closingTime}</Text>
<Text>Address {item.dongAddKor}</Text>
</View>
</TouchableWithoutFeedback>
:
<TouchableWithoutFeedback>
<View>
<Text>{item.branchName}</Text>
<Text>Only showing second part</Text>
</View>
</TouchableWithoutFeedback>
}
selectStore(item) {
this.setState({selectedItem: item});
console.log('repssed');
}
render() {
return(
<ScrollView>
<Header headerText="Costco"/>
<Text>hello</Text>
{this.renderListOfStores()}
</ScrollView>
)
}
}
as a workaround you can have a state that maintain your selected item to expand(or something like another view) and then you can use some conditions to work for render your flat list data.
Consider the below code and let me know if you need some more clarification.
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,FlatList,TouchableNativeFeedback,
View
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
response: [],
selectedItem: {id: null},
};
}
userListLayout() {
const {response} = this.state;
return <FlatList data={response} renderItem={ ({item}) => this.singleUserLayout(item)} keyExtractor={(item) => item.email} extraData={this.state.selectedItem} />
}
singleUserLayout(item) {
return item.id == this.state.selectedItem.id ?
<TouchableNativeFeedback onPress={() => this.userSelected(item)}>
<View style={{flex:1, borderColor: 'black', borderWidth: 1}}>
<Text style={{padding: 10, fontSize: 25}}>{item.email}</Text>
<Text style={{padding: 10,fontSize: 20}}>{item.name}</Text>
</View>
</TouchableNativeFeedback>
: <TouchableNativeFeedback onPress={() => this.userSelected(item)}><Text style={{padding: 10,fontSize: 20}}>{item.email}</Text></TouchableNativeFeedback>
}
userSelected(item) {
console.log(item)
this.setState({selectedItem: item})
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(data => data.json())
.then(response => this.setState({response}))
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>HI THERE</Text>
{this.userListLayout()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});