How to change state value in const mode? - react-native

I am a beginner in React-Native.
I tried to change the state value.
But it seems different with "class" mode. Now I am using "const" mode.
In const mode, how can I change the state value?
import React from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
this.state = {
checkIconURL : require('../../assets/ic_check_circle.png')
}
set = () => {
this.setState({ checkIconURL : require('../../assets/empty.png') }) //But this lines gives err.
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width: 30, height: 30}} />
</TouchableOpacity>
</View>
)
}

state is not available in function components, this.state and this.setState() can only be used in class component, u have to use useState hook from react to use the state...
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [iconUrl, setIconUrl ] = useState(initialState);
const set = () => {
const emptyImageUrl = require('../../assets/empty.png')
setIconUrl(emptyImageUrl);
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {set}>
<Image source = {iconUrl} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}

the following code is incorrect as you need to use Hook in a functional component to manage state.
React provides the useState hook to manage state in a functional component.
import React ,{ useState } from "react";
import {
View,
Text,
Image,
TouchableOpacity
} from "react-native";
const Product = props => {
const initialState = require('../../assets/ic_check_circle.png');
const [ state,setState ] = useState({checkIconURL: initialState});
set = () => {
const emptyImageUrl = require('../../assets/empty.png') });
setState({...state, checkIconURL: emptyImageUrl })
}
return (
<View>
<Text style={{ fontSize: 16}}>Everyday</Text>
<TouchableOpacity onPress = {this.set}>
<Image source = {this.state.checkIconURL} style={{width:
30, height: 30}} />
</TouchableOpacity>
</View>
)
}

Related

Components not rendering on screen react native expo

There are four components on a screen and only three out of four are rendering. <popDown/> is the component that isn't rendering. When I save the specific component's file the components get rendered. Note: I am saving an already fully updated file. It is running with no errors
The Screen
import react from 'react';
import {View,ScrollView} from 'react-native';
import { SafeAreaView, } from 'react-navigation';
import { PrProvider } from '../appFunctions/PrContext';
import PopDown from '../Components/PRForm/popDown';
import RepsWeightTextInput from '../Components/PRForm/RepsWeightTextInput';
import NotesInput from '../Components/PRForm/NotesInput';
import SubmitPr from '../Components/PRForm/SubmitPr';
const PrEnteryScreen = ({}) => {
return(
<View style = {{height:'100%',width:'100%',backgroundColor:'#141212'}}>
<SafeAreaView style = {{alignItems:'center'}}>
{/**
* use context for pr information
*/}
<PrProvider>
<PopDown />
<RepsWeightTextInput/>
<NotesInput/>
<SubmitPr/>
</PrProvider>
</SafeAreaView>
</View>
);
};
export default PrEnteryScreen;
popdown component
import { useContext, useEffect, useState } from "react";
import { TouchableOpacity,StyleSheet,View,LayoutAnimation,UIManager,Platform,useFocusEffect } from "react-native";
import { Children } from "react/cjs/react.production.min";
import SelectExerciseButton, { setIsOpen } from "./SelectExerciseButton";
import SelectExerciseModal from "../../Screens/SelectExerciseModal";
import { EXERCISE_DATA } from "../../Screens/SelectExerciseModal";
import { PrContext, PrProvider } from "../../appFunctions/PrContext";
if (
Platform.OS === "android" &&
UIManager.setLayoutAnimationEnabledExperimental
) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const PopDown = () => {
const value = useContext(PrContext)
const [isOpen,setIsOpen] = useState(false)
const [listHeight,setListHeight] = useState(0)
const [textName,setTextName] = useState(value.exercise);//eventually want to change text based on exercise state
const toggleOpen = () => {
setIsOpen(value => !value);
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
setTextName(value.exercise);
console.log(value.exercise);
}
useEffect(() =>{
EXERCISE_DATA.forEach(()=>{
setListHeight(value => value+50)
})
},[isOpen])
return(
<>
<TouchableOpacity
onPress = {toggleOpen}
>
<SelectExerciseButton />
</TouchableOpacity>
<View>
<SelectExerciseModal style = {isOpen ? styles.show: styles.hidden} listHeight = {listHeight} name = {textName}/>
</View>
</>
)
}
const styles = StyleSheet.create({
hidden:{
height:0,
},
show:{ backgroundColor: '#9B9A9A', width: 200 }
});
export default PopDown;
What I tried
I changed the hidden style height to see if it would render the list that is supposed to pop out of the button. The list did render which means the <selectExerciseButton/> is the only thing not rendering in <popDown/>.
const styles = StyleSheet.create({
hidden:{
height:100,
},
show:{ backgroundColor: '#9B9A9A', width: 200 }
});
<TouchableOpacity
onPress = {toggleOpen}
>
{children}
</TouchableOpacity>
<View>
<SelectExerciseModal style = {isOpen ? styles.show: styles.hidden} listHeight = {listHeight} name = {textName}/>
</View>
<PrProvider>
<PopDown>
<SelectExerciseButton/>
</PopDown>
<RepsWeightTextInput/>
<NotesInput/>
<SubmitPr/>
</PrProvider>
SelectExerciseButton
import { useNavigation,useFocusEffect } from "#react-navigation/native";
import { SafeAreaView, StyleSheet, Text, TouchableOpacity, View,LayoutAnimation } from "react-native";
import { useContext, useState,useEffect } from "react";
import { PrContext } from "../../appFunctions/PrContext";
/**
*
* #returns button that navigates to the a modal to select an exercise
*/
if (
Platform.OS === "android" &&
UIManager.setLayoutAnimationEnabledExperimental
) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const SelectExerciseButton = () => {
const value = useContext(PrContext)
return(
<>
<View style = {styles.background}>
<Text style = {styles.Text}> {value.exercise} </Text>
</View>
</>
);
};
styles = StyleSheet.create({
background:{
backgroundColor: '#9B9A9A',
width: 335,
borderRadius:41,
alignItems:'center',
marginHorizontal:25,
height:200,
justifyContent:'center'
},
Text:{
fontWeight:'bold',
fontSize:40,
color:'white',
alignItems:'center'
}
})
export default SelectExerciseButton;

How to execute onPress on TouchableOpacity react-native using jest and #testing-library/react-native?

I have a component called Header that look like this:
import React from 'react'
import {StyleSheet, TouchableOpacity, View, StyleProp, ViewStyle} from 'react-native'
import {Text} from '..'
import Icon from 'react-native-vector-icons/MaterialIcons'
import {theme} from '#app/presentations/utils/styles'
import {useNavigation} from '#react-navigation/core'
interface IHeaderProps {
title: string
headerRight?: () => JSX.Element | false | undefined
onGoBack?: () => void
hideBackButton?: boolean
style?: StyleProp<ViewStyle>
}
const Header: React.FC<IHeaderProps> = props => {
const navigation = useNavigation()
const goBack = () => {
props.onGoBack ? props.onGoBack : navigation.goBack()
}
return (
<View style={[styles.container, props.style]}>
<View style={styles.leftContent}>
{props?.hideBackButton ? null : (
<TouchableOpacity onPress={goBack} testID="headerBackButton">
<Icon name={'chevron-left'} size={22} color={theme.colors.black} />
</TouchableOpacity>
)}
</View>
<View style={{flex: 1, flexGrow: 10, alignItems: 'center'}}>
<Text maxLines={2} style={{paddingHorizontal: 8, textAlign: 'center'}} type="semibold">
{props.title}
</Text>
</View>
<View style={styles.rightContent}>{props.headerRight && props.headerRight()}</View>
</View>
)
}
export default Header
Focus on TouchableOpacity, I want to fire the onPress of it using testId, but looks like it won't fire.
it('Should have correct behavior', () => {
const goBackFn = jest.fn()
const props: IHeaderProps = {
title: 'My Header',
onGoBack: goBackFn,
}
const {component, getByTestId, queryAllByText} = renderComponent(props)
expect(component).toMatchSnapshot()
expect(queryAllByText('My Header').length).toBe(1)
expect(getByTestId('headerBackButton')).toBeTruthy()
fireEvent.press(getByTestId('headerBackButton'))
expect(goBackFn).toBeCalled()
})
The error message was like this
means that my goBack function never executed. I wondering why.
Then I check the snapshots of my Header component, it is not show TouchableOpacity but it shows View with onClick function on it
<View
accessible={true}
collapsable={false}
focusable={true}
nativeID="animatedComponent"
onClick={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Object {
"opacity": 1,
}
}
testID="headerBackButton"
>
My question is how do I execute onPress on TouchableOpacity ?
I fixed this. At least there is two problem from my implementation.
On the Header component, I forgot to add parenthesis () on props.onGoBack function. It should be props.onGoBack() not props.onGoBack
I need to add await waitFor(() => { ...wait for my getTestById to be truthy })

TouchableOpacity's onPress is not working

Task.js
export const Task = ({ addTask }) => {
const [focusItem, setFocusItem] = useState(null);
return (
<View style={styles.titleContainer}>
<Text style={styles.title}>What would you like to focus on?</Text>
<View style={styles.container}>
<TextInput
style={{ flex: 1 }}
maxLength={50}
value={focusItem}
onSubmitEditing={()=>({ nativeEvent: { text } }) => setFocusItem(text)}
/>
<RoundedButton
style={styles.addSubject}
size={50}
title="+"
onPress={()=>addTask(focusItem)}
/>
</View>
</View>
);
};
App.js
import React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import {Task} from './src/features/task/Task'
export default function App() {
const [task, setTask] = useState(null);
return (
<View style={styles.container}>
{
task ?
(<Text></Text>):
(<Task addTask = {setTask}/>)
}
<Text>{task}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#425670',
},
});
I have tried to send the data from Task to App component by setting the value from Task. But onPress is not working.
I could see the text has been set successfully while executing the onSubmitEditing, but the onPress is doing nothing. Please help me fix this.
Thanks in advance
You need to change this
onSubmitEditing={()=>({ nativeEvent: { text } }) => setFocusItem(text)}
to
onSubmitEditing={({ nativeEvent: { text } }) => setFocusItem(text)}
You could also refer I want to update the parent according to the change of the child to react native

React Native ,Flatlist : how to remove 'unread' note ,once you cliked

I am making a function ,which is at the begining ,all the message are 'unread' .. And once you click the message ,the unread will disappear .like the example as follow:
I had made a component Card.js :
import React from 'react';
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
function Card({
img,
userType,
visible,
onPress,
}) {
return (
<TouchableOpacity style={styles.container} onPress={onPress}>
<View style={styles.container}>
<Image style={styles.img} source={img}/>
{/** userType: 0:teacher 1:student 2.other */}
{userType===0&&(
<Text style={styles.txt}>teacher</Text>
)}
{userType===1&&(
<Text style={styles.txt}>student</Text>
)}
{userType===2&&(
<Text style={styles.txt}>other</Text>
)}
{visible===1&&(
<Text style={styles.txt_notice}>Unread</Text>
)}
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container : {
width : '90%',
flexDirection : 'row',
justifyContent :'center',
alignItems : 'center',
marginTop : 10,
},
txt : {
fontSize : 18,
marginLeft : 20,
},
img : {
width :100,
height : 100,
borderRadius : 50,
},
txt_notice :{
fontSize : 16,
color :'#DC143C',
marginLeft :10,
}
})
export default Card;
and the screen i used TryScreen.js:
import React, { useState,useEffect } from 'react';
import { FlatList, StyleSheet, View } from 'react-native'
import Card from '../components/Card';
const clients = [
{id:1,img:{uri:'https://i.pinimg.com/564x/64/02/cb/6402cbd3d436caecad2f3e300d67ebd1.jpg'},userType:0,visible:1},
{id:2,img:{uri:'https://i.pinimg.com/236x/34/d6/8c/34d68c7694e1c43204c0dbc623ce4cb8.jpg'},userType:1,visible:1},
{id:3,img:{uri:'https://i.pinimg.com/236x/a0/14/26/a014261951fae6dcc16b4d555f0ef089.jpg'},userType:2,visible:1},
{id:4,img:{uri:'https://i.pinimg.com/236x/96/bf/b6/96bfb641920f527e4fcc9ca23501e222.jpg'},userType:2,visible:1},
{id:5,img:{uri:'https://i.pinimg.com/236x/8b/6e/d9/8b6ed9f0cd6de175537879233a017b0c.jpg'},userType:1,visible:1},
{id:6,img:{uri:'https://i.pinimg.com/236x/0e/16/7f/0e167fab5f152a630fd80dfa1ad3e47c.jpg'},userType:0,visible:1},
]
function TyeScreen(props) {
const[clients_list,setClients]=useState([]);
useEffect(()=>{
setClients(clients);
},[]);
return (
<View style={styles.container}>
<FlatList
data = {clients}
keyExtractor = {item =>item.id.toString()}
renderItem = {({item,index})=>(<Card
img = {item.img}
userType = {item.userType}
visible = {item.visible}
onPress = {()=>{
item.visible -=1;
setClients(item);
}}
/>)}
/>
</View>
);
}
const styles = StyleSheet.create({
container : {
flex : 1,
backgroundColor : '#ffffff',
}
})
export default TyeScreen;
My idea is when click the message ,the 'unread' notice will disappear ... I have tried many ways ,but not worked ... Could you please help me take a look my code ,thank you so much !!
Your clients-list need to be in the state, something like
const [clients, setClients] = useState();
then you initialize them in
useEffect(() => {
setClients(...)
})
and then in onPress you change the property of the selected client and change the state again.
Edit
a simple implementation to force re-rendering
onPress = (client) => {
client.unread = -1;
const newclients = {...clients};
setClients(newclients);
}

React Native conditional rendering not working

I want to have a side menu, with a list of categories and when the user selects a category, it should open a scrollable list, right below the category name, with all the pieces of that category.
So I created two list components, one for the categories (SideMenuList) and one for the furniture pieces. I figured I needed to use conditional rendering to render the second list when the user selects the category.
My code:
Side menu code from app.js
state = {
hideMenu: null,
hideList: null
}
sideMenuShow() {
if(!this.state.hideMenu) {
return(
<SideMenu>
<MenuButton onPress = {() => this.setState({hideMenu: true})}/>
<Text style = {{color: 'white', fontSize: 16, fontWeight: 'bold'}}>Furniture</Text>
<SideMenuList onPress = {() => this.setState({hideList: true})}>
{
this.state.hideList ? console.log('yes') : null
}
</SideMenuList>
</SideMenu>
);
}
else {
return(
<SmallSideMenu>
<MenuButton onPress = {() => this.setState({hideMenu: false})}/>
</SmallSideMenu>
);
}
}
SideMenuList.js
import React, { Component } from 'react';
import { View, FlatList, Text, TouchableOpacity } from 'react-native';
import { CardSection } from './common';
import SideMenuItem from './SideMenuItem';
class SideMenuList extends Component {
render() {
return (
<View style = {{flex: 1}}>
<FlatList
style = {{marginBottom: 2}}
data={[
{key: 'Test'},
{key: 'Test2'},
{key: 'Test3'},
{key: 'Test4'},
{key: 'Test5'}
]}
renderItem={({item}) =>
<TouchableOpacity>
<SideMenuItem
onPress = {this.props.onPress}
text={item.key}
>
{this.props.children}
</SideMenuItem>
</TouchableOpacity>}
/>
</View>
);
}
}
export default SideMenuList;
SideMenuItem.js code
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
const SideMenuItem = (props, {onPress}) => {
return (
<View
style = {{flex: 1}}>
<TouchableOpacity
onPress = {onPress}>
<Text style={styles.itemStyle}>{props.text}</Text>
</TouchableOpacity>
{props.children}
</View>
);
}
const styles = {
itemStyle: {
marginTop: 10,
marginRight: 20,
marginLeft: 10,
color: 'white'
}
};
export default SideMenuItem;
My problem right now is that my onPress event is not changing the value of my state property 'hideList', so I can't even check if my solution would actually work. I'm doing a console log when the value is true but it never appears in my console.
Thanks in advance.
You are rendering your SideMenuItem with a TouchableOpacity wrapping it (in your SideMenuList file).
Probably when you are pressing the button, it's triggering SideMenuList button, instead of SideMenuItem button.
Try to remove the TouchableOpacity from SideMenuList and check if it works.
Hope it helps