Toggling Images with a boolean state of a react functional component - reactjs

I have a react function component, that I do not want to convert to a class component. I want it to have the functionality so that when it is pressed, the image is changed. However, currently, it is only showing one of the images (checked.png). What am I doing wrong?
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
{ check } === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;

Remove {} around the check
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
check === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Otherwise it is an object created everytime

Related

How to change color of specific item in FlatList

const [itemsPressed, setItemsPressed] = useState(true);
const changeColor = (x) => {
setItemsPressed(x)
}
If your item has some unique identifying ID/name, you can use that to help you track which one is pressed. (You could also just store the full item itself and do object equality but just a simple id/string is more than enough)
const [pressedItemIds, setPressedItems] = useState({});
const changeColor = useCallback((item) => {
setPressedItemIds({...pressedItemIds, [item.id]: true});
}, []);
<FlatList data={data}
renderItem={({ item }) => (
<TouchableWithoutFeedback onPress={() => changeColor(item)}>
<View
style={backgroundColor: pressedItemIds.hasOwnProperty(item.id) ? '#8b0000': '#ffc6c4'}>
<Text>{data.text}</Text>
</View>
</TouchableWithoutFeedback>
)} />

Passing state from function to component

My entire goal was to navigate from a screen while changing states in the screen I am navigating to. I have successfully done that in a minimal working example, however in my overall project, the screen I am navigating to needs to be passed the state through a couple levels.
I have two examples. In the first example(You must run the examples in IOS or android, you can see what I need to achieve, everything works as it should. You can move from screen3 to the home page and the states change along with the slider button moving.
In the second example, you can see right off the bat I have an error due to my attempt at passing states the same way I do in the original example however there is one more level I need to pass through in this example. You can see by removing line 39 in this demo, it removes the error so obviously I am not passing states correctly. I need to pass states from Home to Top3 to Slider
Here is example 1 and here is example 2 while I have also provided some code below that highlights the differences where the error occurs in the two examples.
Any insight at all is appreciated more than you know! Thank you.
Example1 -> you can see I directly render the slider button which causes zero issues.
const Home = ({ route }) => {
const [isVisile, setIsVisible] = React.useState(true);
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
}
}, [route.params]);
const goToMap = () => {
setComponentToShow("Screen2");
}
const goToList = () => {
setComponentToShow("Screen1");
}
return(
<View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
{whichComponentToShow === 'Screen1' && <ListHome />}
{whichComponentToShow === 'Screen2' && <MapHome />}
<View style={{position: 'absolute', top: 0, left: 0, right: 1}}>
<Slider
renderMap={goToMap}
renderList={goToList}
active={route.params && route.params.componentToShow==='Screen2'|| false}
/>
</View>
</View>
);
}`
Example2 -> You can see I render Slider in a file called Top3, I am struggling to pass these states from Home to Top3 to Slider.
const [isVisile, setIsVisible] = React.useState(true);
const [whichComponentToShow, setComponentToShow] = React.useState("Screen1");
React.useEffect(() => {
if(route.params && route.params.componentToShow) {
setComponentToShow(route.params.componentToShow);
goToMap()
}
}, [route.params]);
const goToMap = () => {
setComponentToShow("Screen2");
}
const goToList = () => {
setComponentToShow("Screen1");
}
return(
<View style={{backgroundColor: '#d1cfcf' ,flex: 1}}>
{whichComponentToShow === 'Screen1' && <ListHome />}
{whichComponentToShow === 'Screen2' && <MapHome />}
<View style={{position: 'absolute', top: 0, left: 0, right: 1}}>
<Top3
renderMap={goToMap}
renderList={goToList}
active={route.params && route.params.componentToShow==='Screen2'|| false}
/>
</View>
</View>
);
}
Top3
export default class Top3 extends React.Component {
goToMap = () => {
this.props.renderMap();
};
goToList = () => {
this.props.renderList();
};
render() {
return (
<View>
<Slider renderMap={this.goToMap.bind(this)}
renderList={this.goToList.bind(this)}
active={active}/>
</View>
);
}
}
from your examples, I think you are not extracting active from props properly.
here is the demo working code your example2 code https://snack.expo.dev/4atEkpGVo
here is the sample code for component Top3
export default class Top3 extends React.Component {
goToMap = () => {
this.props.renderMap();
};
goToList = () => {
this.props.renderList();
};
render() {
const {active=false} = this.props;
return (
<View>
<Slider renderMap={this.goToMap.bind(this)}
renderList={this.goToList.bind(this)}
active={active}/>
</View>
);
}
}
if you want to share states between multiple screens, then you might want to use global stores like react context api or redux instead of passing states to each screen that would be simple

How to show see more in React Native Web

How do I do the "see more" in react native web? My problem is on the onLayout and onTextLayout.
How do I determine to show the "see more" if its too long?
const ReadMoreText = () => {
const [textShown, setTextShown] = useState(false) //To show ur remaining Text
const [lengthMore, setLengthMore] = useState(false) //to show the "Read more & Less Line"
const toggleNumberOfLines = () => {
//To toggle the show text or hide it
setTextShown(!textShown)
}
const onTextLayout = useCallback((e) => {
// setLengthMore(e.nativeEvent.lines.length >= 4) //to check the text is more than 4 lines or not
// console.log(e.nativeEvent);
}, [])
return (
<View style={styles.mainContainer}>
<Text
onLayout={onTextLayout}
numberOfLines={textShown ? undefined : 4}
>
SAMPLE TEXT HERE...
</Text>
{lengthMore ? (
<Text
onPress={toggleNumberOfLines}
>
{textShown ? 'Read less...' : 'Read more...'}
</Text>
) : null}
</View>
)
}
I spent a long time looking into this and my solution was to implement a really simple component (below) which just limits the lines and allows you to show more. Maybe this is enough for you.
If not you could create a ref and use measureInWindow to get the measurement of the text container. ref.measureInWindow((x, y, width, height) => {})
import * as React from 'react'
import { Text } from 'react-native'
export default function ReadMore({ children, numLines = 4, style }) {
const [lines, setLines] = React.useState(numLines)
return (
<>
<Text numberOfLines={lines} style={style}>
{children}
</Text>
{lines === 0 ? (
<Text onPress={() => setLines(numLines)} style={{fontWeight:'bold'}}>
Read Less
</Text>
) : (
<Text onPress={() => setLines(0)} style={{fontWeight:'bold'}}>
Read More
</Text>
)}
</>
)
}
You can simply define a line height and then divide that by the total height of the text to get the number of lines. Here's how I did it.
import { useState, useCallback } from 'react'
import { Platform, LayoutChangeEvent } from 'react-native'
const LINE_HEIGHT = 30
const [numOfLines, setNumOfLines] = useState(0)
const [isInitialLayoutLoaded, setInitialLayoutLoaded] = useState(false)
const onLayout = useCallback((e: LayoutChangeEvent) => {
if (!isInitialLayoutLoaded && Platform.OS === 'web') {
setNumOfLines(e.nativeEvent.layout.height / LINE_HEIGHT)
setInitialLayoutLoaded(true)
}
},
[isInitialLayoutLoaded],
)
Then simply use it like:
<Text onLayout={onLayout}>A very long piece of text</Text>
#Joseph
just update your code
import * as React from 'react'
import { Text } from 'react-native'
export default function ReadMore({ children, numLines = 4, style }) {
const [lines, setLines] = React.useState(numLines)
const [showButton, setShowButton] = React.useState(false)
const onTextLayout = (e) => {
if(e.nativeEvent.lines.length > numLines){
setShowButton(true)
}
}
return (
<>
<Text onTextLayout={onTextLayout} numberOfLines={lines} style={style}>
{children}
</Text>
{showButton && <>
{lines === 0 ? (
<Text onPress={() => setLines(numLines)} style={{fontWeight:'bold'}}>
Read Less
</Text>
) : (
<Text onPress={() => setLines(0)} style={{fontWeight:'bold'}}>
Read More
</Text>
)}</>}
</>
)}

React Native how to navigate to a Screen and reset some of the states?

again, I need some help. I am trying to implement a shopping cart for my application.
The application has the following logic:
BarcodeScreen (params) => MenuScreen loads data from params => CheckoutScreen
The problem is, that I want to have a "Reset" button, to remove all products in the cart.
For that reason on my CheckoutScreen, there is a Reset button, which navigates to the previous screen (MenuScreen) and it needs to reset some of the state variables (the params for loading data must stay).
Here is the MenuScreen code:
function MenuScreen({ route, navigation }) {
const params = route.params;
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [items, setItems] = useState([]);
const [length, setLength] = useState(0);
useEffect( () => {
loadMenu();
}, []);
const loadMenu = async () => {
const response = await MenuApi.getMenu(params.data);
if(!response.ok) Alert.alert(response.data.message);
setLoading(false);
setProducts(response.data.data);
}
const addItem = (item) => {
items.push(item);
setLength(items.length);
}
return (
<>
<View style={styles.activity}>
<ActivityIndicator animating={loading} size="large"/>
</View>
<FlatList
data={products}
...
/>
<View style={styles.activity}>
<Text>You have {length} products in your order</Text>
</View>
<View style={styles.activity}>
<Button title="View Basket" onPress={() => navigation.navigate("Checkout", items)}></Button>
</View>
</>
);
}
That is the CheckoutScreen code:
function CheckoutScreen({ route, navigation }) {
const products = route.params;
return (
<>
<View style={styles.activity}>
<FlatList
data={products}
...
/>
</View>
<View style={styles.activity}>
<Button title="Submit"></Button>
<Button title="Reset" onPress={() => navigation.reset({
index: 0,
items: [],
routes: [{ name: "Menu"}]
})}>
</Button>
</View>
</>
);
}
Now, when I go back to the MenuScreen, the items = 0, but I don't have the params data anymore.
MenuScreen and CheckoutScreen are two different screens each with their own state. If you want both screens to share that state and both manipulate it and for the state to persistent between them, I recommend you to use
React Redux for the job.

react navigation custom tabBarComponent?

the navigationOptions code like that.
static navigationOptions = ({navigation})=>({
tabBarLabel:'查看',
headerTitle:navigation.state.params.title,
tabBarIcon: ({ tintColor,focused }) => (
<Image style={SKIN.tabImage} source={focused?AppImages.MyPost.lookchoose:AppImages.MyPost.look}/>
),
});
this is my Tab componet,how I can get tabBarLabel and tabBarIcon?
export default class Tab extends Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
return (
<TouchableOpacity
key={index}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(color,focused)}
<Text style={{ color }}>{this.props.getLabel()}</Text>
</View>
</TouchableOpacity>
);
};
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
{routes && routes.map(this.renderItem)}
</View>
);
}
}
I custom Tab,now I want use that but some bug show me.
like that,
imagebug
please help me...
try updating the render method with this code:
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
//pass down the route and the index to the renderItem function
{routes && routes.map((route,index) => this.renderItem(route, index) )}
</View>
);
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
let TabScene = {
focused:focused,
route:route,
tintColor:color
};
return (
<TouchableOpacity
key={route.key}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(TabScene)}
<Text style={{ color }}>{this.props.getLabel(TabScene)}</Text>
</View>
</TouchableOpacity>
);
};

Resources