I've been trying to get a UITableView equivalent in React-Native. The screen looks like this (work in progress):
The code for this is quite primitive at the moment:
class SettingsView extends Component {
render() {
return(
<View style={styles.container}>
//more View code
//example of a cell
<TouchableHighlight style={styles.tableViewCell}>
<Text style={styles.cellLabel}>Log Out</Text>
</TouchableHighlight>
</View>
);
}
}
It works fine, I've been trying to create an accessory - an > indicator - for all my cells. Whilst doing that, I stumbled upon a way to create custom component via this code:
class TableViewCell extends Component {
render() {
return (
<TouchableHighlight style={styles.tableViewCell}>
<Text style={styles.cellLabel}>Log Out</Text>
</TouchableHighlight>
);
}
}
Next, I replaced the initial block of code and I came out with this:
class SettingsView extends Component {
render() {
return(
<View style={styles.container}>
//more View code
//example of a cell
//Replaces the previous block of TouchableHighlight
<TableViewCell/>
</View>
);
}
}
Wow. To a native Swift developer that has barely any experience with HTML and JavaScript, this is amazing. I immediately went on quest in an attempt to find out how I might make this reusable. Currently, TableViewCell is hardcoded to have the text "Log Out". Ideally I want to be able to supply the text as an argument for the constructor. This is where I got stuck. Here's what I've tried so far:
Use getAttribute to see if I could extract an attribute that I would pass in whilst declaring the TableViewCell tag.
//when declaring a cell on the screen's render
<TableViewCell titleText="About"/>
//tableViewCell component
render() {
return(
<TouchableHighlight ...>
<Text>{this.getAttribute("titleText")}</Text>
</TouchableHighlight>
);
}
I couldn't get a reference to the titleText that I've declared as part of the tag. Any ideas?
I could be wrong, but I'm pretty sure this is what you need. You can pass properties to components and receive them as this.props:
// when declaring a cell on the screen's render
<TableViewCell titleText="About"/>
// tableViewCell component
render() {
return(
<TouchableHighlight ...>
<Text>{this.props.titleText}</Text>
</TouchableHighlight>
);
}
Related
I am working with react native and expo.cli and would like to know how I can manage to add a handle to a TouchableOpacity, I have the following component:
function MainModal (){
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={this.TouchableOpacity}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
What I want to do is make a reference to this specific TouchableOpacity when it is selected, but when I use a "this" nested in the component I get the following error: Undefined is not an object (Evaluating 'this.TouchableOpacity') I know maybe This question is a bit of a novice and has more to do with how 'this' works in javascript than with React Native itself, however I can't find a way to make a reference to the selected object so that it executes something when selected. How could I do it?
You can use React.useRef() hook to create a reference to that element so you can access then to it. The idea you have of this is not the actual this. This might refer to the global object (in not strict) and might be undefined in strict mode. If you want to use this in a component I recommend you to create a class component instead of a function one. See this in MDN to learn more of it.
Anyway, the use of ref in a functional component would be this:
function MainModal (){
const touchableReference = React.useRef()
const handleTouchableClick = () => {
console.log(touchableReference.current)
//
// Outputs HTMLDivElement...
//
}
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
ref={touchableReference}
onPress={() => handleTouchableClick()}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
)
}
One standard way of passing event handler in functional component is as follows
function MainModal (props){
const onPress1 = useCallback(()=>console.log); // created inside the componet
const {onPress2} = props; // passed from props
const {onPress3} = useSomeHook(); // from context/hook
return(
<>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity
onPress={onPress1}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress2}
>
<Text>Volver</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={onPress3}
>
<Text>Volver</Text>
</TouchableOpacity>
</View>
</>
);
}
The behaviour of this works the same in react and react-native. React functional component is executed during the render phase and you are not suppose to work with this here, given that you do not understand when and where it is called.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
i just start to learn react native i want to make ui like this,
i want the buttons on the image work like radio button, so user just can choose a button and change the button style choosed by user, and bellow is my code
class ButtonOption extends Component {
constructor(props) {
super(props);
this.state = {
data : [],
active : 0
};
}
loadButton(temp){
let result = [];
for (let i =0;i<temp.length;i++){
if(i==this.state.active){
result.push(
<View key={i} style={buttonOption.main}>
<TouchableOpacity style={buttonOption.tabActive} onPress={()=>{this.setState({active:i});alert(this.state.active)}}>
<Text style={buttonOption.labelActive}>{temp[i]}</Text>
</TouchableOpacity>
</View>)
}else{
result.push(
<View key={i} style={buttonOption.main}>
<TouchableOpacity style={buttonOption.tab} onPress={()=>{this.setState({active:i});alert(this.state.active)}}>
<Text style={buttonOption.label}>{temp[i]}</Text>
</TouchableOpacity>
</View>)
}
}
return result;
}
componentDidMount(){
this.setState({data:this.loadButton(this.props.arrButton)})
}
render() {
return (
<View style={{flexDirection:'row',padding:10}}>
{this.state.data}
</View>
);
}
}
export default ButtonOption;
on code above i was try to make button with looping, in looping function i use active as a state for check active button, and add function onPress so everytime user click button, active state will change, but when i run and choose button the active state won't change, what is wronge with my onPress code, can someone help please?
I think I might know the answer, You have generated the view using this.loadButton and saved it in this.state.data. This is done one time only. when the render method is called. It is expecting a new view based on states but this.state.data is not changing since you create it in componentDidMount. What you need to do is to call this.loadButton(this.props.arrButton) in render method. this will create new view every time render method is called.
render() {
return (
<View style={{flexDirection:'row',padding:10}}>
{this.loadButton(this.props.arrButton)}
</View>
);
}
This might do it, If you still have any error, please report.
I want to build an album app with react native, the main idea is to use a sectionList and show the data in an array like this:
class CustomImage extends Component{
render(){
return(
<View>
<Image style={styles.Img} source={this.props.imageName} />
<Text style={styles.text}>{this.props.name}</Text>
</View>
);
}
}
export default class DisplayAnImage extends Component {
render() {
return (
<View>
<SectionList
sections={[
{titile: 'small soldier', data: ["./gifs/2.gif", "./gifs/3.gif", "./gifs/4.gif"]}
]}
renderItem={({item}) => <CustomImage name={item} fromWeb={false} imageName={require(item)} />}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
/>
</View>
);
}
}
Above code was indented to make it work inspirited by this answer
I have already put the gifs folder under the project folder, and if I using a static string as source={require(url)} in the Image component, things will work, but when the url came out of an array items iteration, it will not work.
How can I make this work with react native?
EDIT
Don't know if I could edit to make it more specificly, the really thing I want to do is to use a generated array like this:
function numberRange(start, end){
return new Array(end-start).fill().map((d,i) => {
var url = "./gifs/" + ( i+start) + ".gif";
return require(url)
});
}
export default class DisplayAnImage extends Component {
render() {
return (
<View>
<SectionList
sections={[
{title: 'small soldier', data: numberRange(2,30)},
{title: 'small soldier', data: numberRange(31,60)}
]}
renderItem={({item}) => <CustomImage name={item} fromWeb={false} imageName={item} />}
renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.title}</Text>}
/>
</View>
);
}
}
Don't know if have a way to use this array generator to make an array or I have to enter the path one by one on bare hand :-(
require is a javascript keyword with a preload nature.
And, images will not related to path at runtime --- provide a path to it will not get anything. it becomes bundle resources, so with console.log you can only see resource token related to bundle ex: _1 _2.
It will not work even change require("./gifs/1.gif") to require("./gifs/"+"1.gif").
Try this:
data: [require("./gifs/2.gif"), require("./gifs/3.gif"), require("./gifs/4.gif")]
I'm trying to load some local images in a simple app that I'm making using react native.
I'm following the guide here - https://facebook.github.io/react-native/docs/images.html
Unfortunately, that guide says the following:
Note that in order for this to work, the image name in require has to be known statically.
Which is why my code isn't working when I try to render rows in a list view:
renderRow(chat){
return (
<View style={ styles.row }>
<Image
source={require(`../images/${chat.image}`)} //this is the part that's not working. It can't require a dynamic path
style={ styles.cellImage }
/>
</View>
);
}
How can I achieve this? Or can you suggest another way that will work?
It's quirky and odd, but if you have a set number of options for the images (which makes sense if the images are living in the project) then you can return the statically required image based on a given condition.
getSource() {
if (condition) {
return require( '../images/option-one' );
} else {
return require( '../images/default-option' );
}
}
renderRow(chat){
return (
<View style={ styles.row }>
<Image
source={ this.getSource() }
style={ styles.cellImage }
/>
</View>
);
}
Here is my render function of react native. if i put listview it works. if i put touchablehighlight it works. but, if it put both it doesn't work. need help.
render: function() {
return (
/* ListView wraps ScrollView and so takes on its properties.
With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.*/
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}/>
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
</TouchableHighlight>
);
},
what is wrong here? need both components to work.
You can't have 2 tags that you can return. You should wrap it inside a <View> </View> tag. This way you can abstract multiple components that you need in the page.
render: function() {
return (
/* ListView wraps ScrollView and so takes on its properties.
With that in mind you can use the ScrollView's contentContainerStyle prop to style the items.*/
<View>
<ListView
contentContainerStyle={styles.list}
dataSource={this.state.dataSource}
renderRow={this._renderRow}/>
<TouchableHighlight onPress={() => this._pressRow(rowID)} underlayColor="transparent">
</TouchableHighlight>
</View>
);
},
Hope it helps.