Want to change opacity with react native refs on click - react-native

Here is my code. I want to change the opacity of refs when i click on any TouchableOpacity component.Please guide me how i can change opacity or change colour in react native with refs.
When i click my redirect function calls so i wanna change the opacity of particular ref in redirect function, i am passing ref and routename is redirect function.
i
mport React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet
} from 'react-native';
export default class Navigation extends Component {
redirect(routeName,ref)
{
console.log(this.refs[ref]]);
this.props.navigator.push({
ident: routeName
});
}
render() {
return (
<View style={style.navigation}>
<View style={[style.navBar,styles.greenBack]}>
<TouchableOpacity style={style.navPills} onPress={ this.redirect.bind(this,"AddItem","a")} ref="a">
<Text style={[style.navText,style.activeNav]}>HOME</Text></TouchableOpacity>
<TouchableOpacity style={style.navPills} onPress={ this.redirect.bind(this,"AddItem","b")} ref="b">
<Text style={style.navText}>ORDER</Text></TouchableOpacity>
<TouchableOpacity style={style.navPills} onPress={ this.redirect.bind(this,"ListItem","c")} ref="c">
<Text style={style.navText}>SHOP LIST</Text></TouchableOpacity>
<TouchableOpacity style={style.navPills} onPress={ this.redirect.bind(this,"ListItem","d")} ref="d">
<Text style={style.navText}>DUES</Text></TouchableOpacity>
</View>
<View style={style.titleBar}>
<Text style={style.titleBarText}>{this.props.title}</Text>
</View>
</View>
);
}
}
const style = StyleSheet.create({
navigation:{
top:0,
right:0,
left:0,
position:'absolute'
},
navBar:{
flexDirection:'row',
padding:10,
paddingTop:15,
paddingBottom:15,
},
navPills:{
flex:1,
alignItems:'center'
},
navText:{
flex:1,
textAlign:'center',
fontSize:16,
fontWeight:'bold',
color:'#ffffff',
opacity:0.7
},
titleBar:{
backgroundColor:'#ffffff',
flex:1,
padding:8,
alignItems:'center',
borderBottomWidth:1,
borderBottomColor:'#dddddd'
},
titleBarText:{
fontSize:18
},
activeNav:{
opacity:1
}
});

I am not exactly sure if the following is what u are searching:
If you want to change the opacity of the TouchableOpacity use the following
export default class Navigation extends Component {
state={
opacity: 0.1
}
handleOnPress = () => {
this.setState({
opacity: 0.5 //Anything u want
});
}
render(){
return(
<TouchableOpacity underlayColor={'rgba(0,0,0,this.state.opacity)'} onPress={this.handleOnPress}>
)
}
}
If you want to change the opacity of your text use the following
export default class Navigation extends Component {
state = {
opacity: 0.1
}
handleOnPress = () => {
this.setState({
opacity: 0.5 //Anything u want
});
}
render(){
return(
<TouchableOpacity onPress={this.handleOnPress}>
<Text style={[style.navText, {opacity: this.state.opacity}]}>DUES</Text>
</TouchableOpacity>
)
}
}
Using the Stylemethods in the render allows you to take variables from the state
Hope this is the answer you wanted. If One of both is the right let me know and i delete the other one.
Best Regards

Put your opacity value into state. Then make the button click change the value of that state. This will trigger a re-render and your view will update with the new opacity.

To expand on the answer from pomo...
With the styles as you currently have them, you can easily call setState within each of your onPress functions to change the opacity of the elements you need changed. You don't even need to pass a reference if you utilize a different key in the state for each item.
Then, in your styles you would use an array of styles to use the opacity value from the state.
style={[style.navPills, { opacity: this.state.opacityA }]}

I'm not a fan of inline styles at all. So, for my purposes in a recent project I set the style of an element using its 'ref' value, then triggered a state change merely to cause the render function to be called. This is what I believe you're asking for and this sample code should point you in the right direction, otherwise perhaps this will help someone else in the future.
toggleDisplay() {
if (this.refs.blah.style.display === "") { // currently visible
this.refs.blah.style.display = "none";
this.setState({showBlah = false});
} else { // currently not visible
this.refs.blah.style.display = "";
this.setState({showBlah: true});
}
}
render() {
// Some element defined with the ref value used above.
return (<div>
<div ref="blah">Now you see me...</div>
<button onClick="this.toggleDisplay">Toggle Me</button>
</div>);
}
Nothing in my render function changed by adding the toggle functionality, other than adding a button somewhere to call the function. As I already indicated, that state value is only used to trigger the render process.

Related

FlatList ref scrollToIndex is not a function

I am facing what seems to be a long-lasting issue in react native.
I am using Expo SDK35 with RN version 0.59. I have not updated to Expo SDK36 / RN 0.60 yet, due to large code base, but I could update if that makes up for a solution to my issue.
I have an Animated.View component that has a FlatList child, and I am unable to use the static methods (scrollToIndex() in particular) that should be available on the FlatList reference. See the next example code:
class Example extends React.Component{
constructor(props){
super(props);
this.myRef = null;
}
componentDidUpdate = () => {
/*
somewhere in code outside this class, a re-render triggers
and passes new props to this class.
I do have props change detection, and some more other code,
but I have removed it in order to minimize the code example here
*/
// This call throws:
// TypeError: undefined is not a function (near '...this._scrollRef.scrollTo...')
this.myRef.scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
// Other suggested solution from SO
// This also throws:
// TypeError: _this.myRef.getNode is not a function. (In '_this.myRef.getNode()', '_this.myRef.getNode' is undefined)
this.myRef.getNode().scrollToIndex({
animated: true,
index: 1,
viewOffset: 0,
viewPosition: 0.5
});
}
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<FlatList ref={(flatListRef) => { this.myRef = flatListRef; }}
// more FlatList related props
/>
</Animated.View>
}
I have tried to use Animated.FlatList instead, still throws the same errors as in the code example above.
I have also tried to use react native's findNodeHandle() utility function on the received flatListRef parameter, but it returns null.
I have found the same issue posted multiple times in the past here on Stack Overflow, most with no answer, or which do not work for me. These posts are also a bit old (a year or so), which is why I am posting again for the same issue.
Did anyone manage to find a solution/workaround for this issue?
EDIT: Possible workaround
As I was playing with code, I tried to use a ScrollView component instead of FlatList - and the scrollTo method works!
The changes were only on the FlatList - ScrollView specific props (so, for a ScrolLView it would be childs instead of data={[...]} and renderItem={()=>{ ... }}, ect.), and the scrollToIndex method in componentDidMount which was replaced by scrollTo.
The render method of the class, with a ScrollView, now looks like this:
render = () => <Animated.View style={{ /* ... some animated props */ }}>
<ScrollView ref={(flatListRef) => { this.myRef = flatListRef; }}>
{/*
this.renderItem is almost the same as the
renderItem method used on the FlatList
*/}
{ this.state.dataArray.map(this.renderItem) }
</ScrollView>
</Animated.View>
Please note that ScrollView does not have a scrollToIndex() method, so you'll have to cope with manually keeping track of child positions, and maybe, implement a scrollToIndex method of your own.
I am not making this the answer to my question, because the underlying issue remains. But as a workaround, maybe you can go with it and call it a day...
TL;DR;
this.myRef = React.createRef();
this.myRef.current.doSomething(); // note the use of 'current'
Long version:
While the idea behind what I was trying was correct, the error in my original post seems to be quite stupid. In my defense, the docs were not clear (probably...). Anyway...
React.createRef returns an object with a few fields on it, all of them useless for the developer (used by React in the back) - except one: current.
This prop holds the current reference to the underlying component that the ref is attached to. The main ref object is not usable for the purpose I meant to in my original question above.
Instead, this is how I should've used the ref correctly:
this.myRef.current.scrollToIndex(...)
Hold up, don't crash
Both the main myRef object, and the current field will be null if the component has not yet mounted, has unmounted at any point later, or if the ref cannot be attached to it for some reason. As you may know (or found out later), null.something will throw an error. So, to avoid it:
if ((this.myRef !== null) && (this.myRef.current !== null)){
this.myRef.current.scrollToIndex(...);
}
Extra insurance
If you try to call an undefined value as a function on a field on the ref, your code will crash. This can happend if you mistakenly reuse the same ref on multiple components, or if the component you attached it to does not have that method (i.e. View does not have a scrollTo method). To fix this you have two solutions:
// I find this to be the most elegant solution
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
this.myRef.current.scrollToIndex(...);
}
}
or
if ((this.myRef !== null) && (this.myRef.current !== null)) {
if (typeof this.myRef.current.scrollToIndex === "function") {
try {
this.myRef.current.scrollToIndex(...);
} catch (error) {
console.warn("Something went wrong", error);
}
}
}
I hope this to be useful for anyone else learning to use refs in React. Cheers :)
With Animated.ScrollView:
Create a ref to your FlatList (the old way only works):
<ScrollView ref={ (ref) => (this.MyRef=ref) } />
Access scrollToIndex using this.myRef.getNode().scrollToIndex
Animated.FlatList is currently not working unfortunately...
With FlatList:
Create a ref to your FlatList by:
<FlatList ref={ this.flatListRef } />
constructor(props) {
super(props);
this.flatListRef = React.createRef();
}
Access scrollToIndex using this.flatListRef.current.scrollToIndex
Also make sure to wrap your code inside an if statement like:
if (this.myRef.getNode()) { this.flatListRef.getNode().scrollToIndex(); }
o do not know if this will help you... it scroll to a especific item in the list:
/*Example to Scroll to a specific position in scrollview*/
import React, { Component } from 'react';
//import react in our project
import {
View,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
Image,
TextInput,
} from 'react-native';
//import all the components we needed
export default class App extends Component {
constructor() {
super();
//Array of Item to add in Scrollview
this.items = [
'zero',
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'ten ',
'eleven',
'twelve',
'thirteen',
'fourteen',
'fifteen',
'sixteen',
'seventeen',
'eighteen',
'nineteen',
'twenty ',
'twenty-one',
'twenty-two',
'twenty-three',
'twenty-four',
'twenty-five',
'twenty-six',
'twenty-seven',
'twenty-eight',
'twenty-nine',
'thirty',
'thirty-one',
'thirty-two',
'thirty-three',
'thirty-four',
'thirty-five',
'thirty-six',
'thirty-seven',
'thirty-eight',
'thirty-nine',
'forty',
];
//Blank array to store the location of each item
this.arr = [];
this.state = { dynamicIndex: 0 };
}
downButtonHandler = () => {
if (this.arr.length >= this.state.dynamicIndex) {
// To Scroll to the index 5 element
this.scrollview_ref.scrollTo({
x: 0,
y: this.arr[this.state.dynamicIndex],
animated: true,
});
} else {
alert('Out of Max Index');
}
};
render() {
return (
<View style={styles.container}>
<View
style={{
flexDirection: 'row',
backgroundColor: '#1e73be',
padding: 5,
}}>
<TextInput
value={String(this.state.dynamicIndex)}
numericvalue
keyboardType={'numeric'}
onChangeText={dynamicIndex => this.setState({ dynamicIndex })}
placeholder={'Enter the index to scroll'}
style={{ flex: 1, backgroundColor: 'white', padding: 10 }}
/>
<TouchableOpacity
activeOpacity={0.5}
onPress={this.downButtonHandler}
style={{ padding: 15, backgroundColor: '#f4801e' }}>
<Text style={{ color: '#fff' }}>Go to Index</Text>
</TouchableOpacity>
</View>
<ScrollView
ref={ref => {
this.scrollview_ref = ref;
}}>
{/*Loop of JS which is like foreach loop*/}
{this.items.map((item, key) => (
//key is the index of the array
//item is the single item of the array
<View
key={key}
style={styles.item}
onLayout={event => {
const layout = event.nativeEvent.layout;
this.arr[key] = layout.y;
console.log('height:', layout.height);
console.log('width:', layout.width);
console.log('x:', layout.x);
console.log('y:', layout.y);
}}>
<Text style={styles.text}>
{key}. {item}
</Text>
<View style={styles.separator} />
</View>
))}
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 30,
},
separator: {
height: 1,
backgroundColor: '#707080',
width: '100%',
},
text: {
fontSize: 16,
color: '#606070',
padding: 10,
},
});
if i completly wrong, tell me...
Because ScrollView has no scrollToOffset function and It has only scrollTo function.
So let use function scrollTo with ScrollView or scrollToOffset with FlatList and it works normal.
If you are working with 'KeyboardAwareFlatList' this worked nicely:
https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/372
In short, use useRef and use the innerRef property of the KeyboardAwareFlatList rather than the ref property.

Only button text area is clickable

Clicking does not work for all parts of the button. Only the text area is clickable.
As a solution, I used contentStyle instead of style prop. But it changes only the color in the touching space of the button. I need to apply button click for the whole button and to change the color of the whole button when clicking on anywhere of the button.
Here is my code:
import * as React from "react";
import { Button } from "react-native-paper";
import styles from "./styles";
const Cbutton = ({ text, onPress }) => (
<Button style={styles.wrapper} mode="contained" onPress={onPress}>
{text}
</Button>
);
export default Cbutton;
This is my code for the stylesheet.
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
wrapper: {
flexDirection: 'row',
justifyContent:'center',
alignItems: 'center',
width: ( "96%" ),
},
});
I had this same problem, and thankfully the fix is super simple - just remove { alignItems: 'center' } from your Button's style prop 👍
When applied, it shrinks the Button's content container, and it's unnecessary anyways thanks to the Button's internal styling.
For height, I recommend setting { height: '100%' } in the contentStyle prop.
Not sure about customizing the onPress color, however. If you can't find a ready-made solution, I'd try rolling your own using react-native Pressable.
You must use TouchableHighlight to change on the active state.
As for as, the clickable area is concerned I think rn-paper button by default is proper. You must check the way you had exported the component.
The issue described by #os-hewawitharana just happen after you set the disabled to true and then set back to false.
Here is how to simulate the issue. In constructor the button is enabled, therefore you can tap all the button area, after disabling and reenabling his state you will only can tap in text area. There is nothing wrong with component and his export method.
constructor(props) {
super(props);
this.state = {
desativado: false
};
}
async componentDidMount() {
setTimeout(() => {
this.setState({ desativado: true });
}, 2000);
setTimeout(() => {
this.setState({ desativado: false });
}, 4000);
}
render(){
return (
<Button
label={'Entrar'}
color={'blue'}
onPress={async () => {
await this.setState({ desativado: true });
}}
disabled={this.state.desativado}
mode="contained"
ark={true} > <Text style={{ fontSize: 14 }}>Text</Text>
</Button>
);
For while the solution is use a version 3 alpha
npm i react-native-paper#3.0.0-alpha.3
Soon they will release a v3 stable release as they answer in my issue report in github: https://github.com/callstack/react-native-paper/issues/1297

Create a reusable React Native Modal Component

I'm going back to basics with React Native, as I feel overwhelmed. I have been looking for an implementation of a reusable modal component. I'm looking for examples of a reusable Modal component in RN? Thanks in advance
You can find many examples of this on StackOverflow. Still, if you need example I can help you with one example. You have mentioned modal component in your question, right?
Your component will look like this with props. let the name be ModalComponent for this file.
render() {
const { isVisible, message, textValue } = this.props;
return (
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
<Text>textValue</Text>
<Text>message</Text>
</View>
</Modal>
);
}
so now in your js file you need to import this modalComponent and after that, you need to write as
<ModalComponent
isVisible={true}
textValue={'hi there'}
message={'trying to make a basic component modal'}/>
Hope this will help for you
EDIT:
Create seperate components that you want to render inside modal. for Ex: component1.js, component2.js, component3.js with props
component1.js:
render(){
const { textVal, message } = this.props
return (
<View>
<Text>{textVal}</Text>
<Text>{message}</Text>
</View>
)
}
now in ModalComponent
render() {
const { first, second, third, isVisible, component1Text, component1Message } = this.props;
<Modal
animationType="slide"
transparent={false}
isVisible={isVisible}
backdropColor={"white"}
style={{ margin: 0 }}
onModalHide={() => {}}>
<View>
{first && <component1
textValue= component1Text
message= component1Message />}
{second && <Component2 />}
{third && <Component2 />}
</View>
</Modal>
In this way, you can achieve it within the single modal.
You will make a component like this giving the parent component all the liberty to change it through props.
render() {
const { isVisible, message, textValue, animationType, backDropColor, style, onModalHide, children } = this.props;
return (
<Modal
animationType= {animationType || 'slide'}
transparent={transparent || false}
isVisible={isVisible || false}
backdropColor={backdropColor || "white"}
style={[modalStyle, style]}
onModalHide={onModalHide}>
{children}
</Modal>
);
}
Then in your parent component, you need to import this component like this:
import ModalComponent from '../ModalComponent'; //path to your component
<ModalComponent isVisible={true}>
<View>
//any view you want to be rendered in the modal
</View>
</ModalComponent>
I had a lot of troubles using react-native modal, sometimes i started the app and could not close it even when i set the isVisible prop to false, it is even worst on IOs, i did a research and these packages are not being maintained properly.
You will save a lot of time by using a top-level navigator like is recommended in the modal docs: https://facebook.github.io/react-native/docs/modal.
I tried https://github.com/react-native-community/react-native-modal but had the same problems because its an extension of the original react-native modal.
I suggest you to use the react-navigation modal as described here: https://reactnavigation.org/docs/en/modal.html#docsNav
You can refer the following code to write Modal component once and use multiple times.
Write once:
import React, { Component } from 'react';
import { View, Text, Button, Modal, ScrollView, } from 'react-native';
export class MyOwnModal extends Component {
constructor(props) {
super(props);
this.state = {
}
render() {
return(
<Modal
key={this.props.modalKey}
transparent={this.props.istransparent !== undefined ? true : false}
visible={this.props.visible}
onRequestClose={this.props.onRequestClose}>
<View style={{
//your styles for modal here. Example:
marginHorizontal: width(10), marginVertical: '30%',
height: '40%', borderColor: 'rgba(0,0,0,0.38)', padding: 5,
alignItems: 'center',
backgroundColor: '#fff', elevation: 5, shadowRadius: 20, shadowOffset: { width: 3, height: 3 }
}}>
<ScrollView contentContainerStyle={{ flex: 1 }}>
{this.props.children}
</ScrollView>
</View>
</Modal>
);
}
}
Now,
You can call your Modal like following example: (By doing this, you avoid re-writing the Modal and its outer styles everytime!)
Example
<MyOwnModal modalKey={"01"} visible={true} onRequestClose={() =>
this.anyFunction()} istransparent = {true}>
<View>
// create your own view here!
</View>
</MyOwnModal>
Note: If you are in using different files don't forget to import , and also you can pass the styles as props.
(You can create/customise props too based on your requirement)
Hope this saves your time.
Happy coding!
I am a contributor of react-native-use-modal.
This is an example of creating a reusable modal in a general way and using react-native-use-modal: https://github.com/zeallat/creating-reusable-react-native-alert-modal-examples
With react-native-use-modal, you can make reusable modal more easily.
This is a comparison article with the general method: https://zeallat94.medium.com/creating-a-reusable-reactnative-alert-modal-db5cbe7e5c2b

How do I prevent a user clicking through an overlay?

I have an overlay positioned absolute, it has backgroundColor and it covers the whole screen. It's overlaying several button components which I can still click on through the overlay.
How do I prevent this behavior? I want to swallow all click events landing on the overlay first.
Code:
// Overlay
export default class Overlay extends Component {
render() {
return (
<View style={styles.wrapper} />
);
}
}
const styles = StyleSheet.create({
wrapper: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: "black",
opacity: 0.7
}
});
// Container
export default class Container extends Component {
render() {
return (
<View>
<Overlay />
<Button onPress={() => this.doSomething()}>
<Text>Hello</Text>
</Button>
</View>
);
}
}
Write the absolute position component after other components to render it over the other components.
export default class Container extends Component {
render() {
return (
<View>
<Button onPress={() => this.doSomething()} title="Hello" />
<Overlay /> // provide appropriate height and width to the overlay styles if needed...
</View>
);
}
}
Solution 1- You can try to use Modal component from react-native
Solution 2- Wrap TouchableWithoutFeedback having blank onPress around your overlay. Don't forget to give full height and width to TouchableWithoutFeedback
something like
<TouchableWithoutFeedback>
<Overlay/>
<TouchableWithoutFeedback/>

setNativeProps Change Value for Text Component React Native Direct Manipulation

I want to directly update the value of a component due to performance reasons.
render(){
<View>
<Text style={styles.welcome} ref={component => this._text = component}>
Some Text
</Text>
<TouchableHighlight underlayColor='#88D4F5'
style={styles.button}>
<View>
<Text style={styles.buttonText}
onPress={this.useNativePropsToUpdate.bind(this)}>
Iam the Child
</Text>
</View>
</TouchableHighlight>
</View>
}
This is the method I use to update the text component. I dont know if I am setting the right attribute/ how to figure out which attribute to set:
useNativePropsToUpdate(){
this._text.setNativeProps({text: 'Updated using native props'});
}
Essentially trying to follow the same approach from this example:
https://rnplay.org/plays/pOI9bA
Edit:
When I attempt to explicitly assign the updated value:
this._text.props.children = "updated";
( I know this this the proper way of doing things in RN ). I get the error "Cannot assign to read only property 'children' of object'#'"
So maybe this is why it cant be updated in RN for some reason ?
Instead of attempting to change the content of <Text> component. I just replaced with <TextInput editable={false} defaultValue={this.state.initValue} /> and kept the rest of the code the same. If anyone know how you can change the value of <Text> using setNativeProps OR other method of direct manipulations. Post the answer and ill review and accept.
The text tag doesn't have a text prop, so
this._text.setNativeProps({ text: 'XXXX' })
doesn't work.
But the text tag has a style prop, so
this._text.setNativeProps({ style: { color: 'red' } })
works.
We can't use setNativeProps on the Text component, instead, we can workaround and achieve the same result by using TextInput in place of Text.
By putting pointerEvent='none' on the enclosing View we are disabling click and hence we can't edit the TextInput (You can also set editable={false} in TextInput to disbale editing)
Demo - Timer (Count changes after every 1 second)
import React, {Component} from 'react';
import {TextInput, StyleSheet, View} from 'react-native';
class Demo extends Component {
componentDidMount() {
let count = 0;
setInterval(() => {
count++;
if (this.ref) {
this.ref.setNativeProps({text: count.toString()});
}
}, 1000);
}
render() {
return (
<View style={styles.container} pointerEvents={'none'}>
<TextInput
ref={ref => (this.ref = ref)}
defaultValue={'0'}
// editable={false}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 0.7,
justifyContent: 'center',
alignItems: 'center',
},
textInput: {
fontSize: 60,
width: '50%',
borderColor: 'grey',
borderWidth: 1,
aspectRatio: 1,
borderRadius: 8,
padding: 5,
textAlign: 'center',
},
});
export default Demo;
As setNativeProps not solving the purpose to alter the content of <Text />, I have used below approach and is working good. Create Simple React Component like below...
var Txt = React.createClass({
getInitialState:function(){
return {text:this.props.children};
},setText:function(txt){
this.setState({text:txt});
}
,
render:function(){
return <Text {...this.props}>{this.state.text}</Text>
}
});