Change only one icon in a list of objects - React Native - react-native

I want to be able to change the icon in a list of todos (see picture) from an exclamation mark, to a checkmark. That should happen if the user puts the finger on the icon, or the developer clicks with the mouse in the emulator.
Through the code below, I manage to change it, but the new icon only appears if I close the modal containing the list, and reopen it. So the modal does not re-render, neither partly nor in whole.
How can I make the changes appear live, immediately after I click the exclamation icon? I suspect it has to do with state, but it doesn't seem possible to create a React hook inside the map function. If I let onPress call a function, then the state is only known within that external function, and I don't know how to export it.
export const TeacherMessages = (props) => {
return (
<View
style={[
styles.borderBox,
props.todos.length > 0 || props.notes.length > 0
? styles.whiteBox
: null,
]}
>
{
props.todos.map((todo) => (
<View key={todo.id} style={styles.listNotes}>
<AntDesign
style={styles.listIcon}
onPress={() => todo.isChecked = true}
name={todo.isChecked ? "checksquare" : "exclamationcircle"}
color={todo.isChecked ? "green" : "red"}
size={18}
/>
<Text style={styles.listText}> {todo.description}</Text>
</View>
))
}
);

I think you need to store the todos array in a react hook so that way the changes you do to it becomes live instantly, You can have this changeTodo function in the parent component and pass it as props to call it from the child component with the index needed. I think this might help:
export const TeacherMessages = (props) => {
const [todosArr, setTodosArr] = React.useState(props.todos)
const checkTodo = (todoIndex) =>{
let arr = [...todosArr]
arr[todoIndex].isChecked= true
setTodosArr(arr)
}
return (
<View
style={[
styles.borderBox,
todosArr.length > 0 || props.notes.length > 0
? styles.whiteBox
: null,
]}
>
{
todosArr.map((todo, index) => (
<View key={todo.id} style={styles.listNotes}>
<AntDesign
style={styles.listIcon}
onPress={() => checkTodo(index)}
name={todo.isChecked ? "checksquare" : "exclamationcircle"}
color={todo.isChecked ? "green" : "red"}
size={18}
/>
<Text style={styles.listText}> {todo.description}</Text>
</View>
))
}
);

Related

creating component in onPress event

I'm trying to build an android app using functional react-native and expo.
I want to know to create new components inside other components when events are triggered.
What I'm trying to do is the following
The code I wrote is below:
<View>
<TouchableOpacity style = {styles.container} onPress = {() => {return (
<TouchabeOpacity>
<Text> Hello World </Text>
</TouchabeOpacity> ) }}
</ToucchabeOpacity>
</View>
I want when pressing the first touchableopacity another one would be created and displayed.
Any solutions ?
You need to do something like:
import {NewComponent} from './components/NewComponent';
....
const[displayNewComponent, setDisplayNewComponent] = useState(false)
...
<View>
<TouchableOpacity style = {styles.container} onPress={() => setDisplayNewComponent(true)}
{ displayNewComponent && <NewComponent /> }
</ToucchabeOpacity>
</View>
export const NewComponent = () => {
return (
<TouchabeOpacity>
<Text> Hello World </Text>
</TouchabeOpacity>
)

React native move flatlist from one screen to another

I have already done firebase with user authentication. Each result is displayed in a flatlist. Upon clicking the heart icon I want to move the selected item to the favorite screen.
Home Screen
<FlatList
contentContainerStyle={ { alignSelf:"center",}}
data={Results}
keyExtractor={(Result) => Result.id.toString()}
renderItem={({ item }) => {
// console.log(item.id);
return (
<TouchableOpacity
onPress={() => {
navigation.navigate("ResultsShowScreen", {
id: item.id,
image: item.image,
});
}}
>
<ResultsDetail Result={item} />
</TouchableOpacity>
);
}}
/>
</View>
Favorites Screen
<View>
<Text> Screen</Text>
</View>
You can achieve this by reading the properties of the route.params object of your Favorite Screen. Like so:
function Favorites ({route}) {
const {id, image} = route.params;
return (
...
);
}
Keep in mind that this will only show the id and image you are passing when the TouchableOpacity is pressed. If you want a list of all the favorites, you need to store them on a data structure like an array (and modify the array when an item is faved/unfaved), and pass that array to the Favorite Screen
For the complete reference of this feature, you can read the Passing parameters to routes page of React Navigation docs.

OnPress change the style of component from loop- React Native with hooks

So I am pretty new in react native, I am trying to develop a quiz game, where users will be given Set of answers. I want to select change the color of the component when it is pressed by the user, kind of toggle it. So far I came up with useState solution, but unfortunately cannot figure out how to exclude the change of color, I guess I need to follow indexing or something, can anyone please make me understand the process with the solution.
export const QuizScreen = ({ navigation,route }) => {
const [quizArray, setQuizArray] = React.useState([])
const [rightAnswer, setRightAnswer]= React.useState(false)
const [selectBtn, setSelectBtn] = React.useState("#fff")
return(
<View>
{quizArray[qno].answer.map(r=>
<TouchableHighlight style={[styles.listItem, {backgroundColor:selectBtn}]}
onPress={()=>{
setRightAnswer(r.rightAnswer)
setSelectBtn("#DDDDDD") //so this changes logically all the component from the list
}}
activeOpacity={0.6} underlayColor="#DDDDDD"
>
<Text>{r.option}</Text>
</TouchableHighlight>
)}
</View>
I need to know how do i implement the background change for only one and kinda make it toggle everytime user select or deselect. Thank you
You were right about using an index for determining the clicked list item.
You can change the color by storing the index of the selected item using selectBtn state and then using that state set the backgroundColor accordingly.
Here is how you can do it:
export const QuizScreen = ({ navigation, route }) => {
const [quizArray, setQuizArray] = React.useState([]);
const [rightAnswer, setRightAnswer] = React.useState(false);
const [selectBtn, setSelectBtn] = React.useState(null);
return (
<View>
{quizArray[qno].answer.map((r, index) => (
<TouchableHighlight
style={[
styles.listItem,
{ backgroundColor: selectBtn === index ? '#dddddd' : '#fff' },
]}
onPress={() => {
setRightAnswer(r.rightAnswer);
setSelectBtn(index);
}}
activeOpacity={0.6}
underlayColor="#DDDDDD">
<Text>{r.option}</Text>
</TouchableHighlight>
))}
</View>
);
};
Here is the working example: Expo Snack
2

DoneButton is not rendering in 'react-native-app-intro-slider-rerender-on-prop-change'

I am trying to make a multiple choice test with a few questions using React Native package 'react-native-app-intro-slider'. The problem was that this package was not rerendering the items when something changes, state for example. I found a similar or refactored package called 'react-native-app-intro-slider-rerender-on-prop-change' that did the same thing but rerendered items on a state change. But the problem is that it does not render the Done button at last slide.
The last package i used was this :
import AppIntroSlider from 'react-native-app-intro-slider-rerender-on-prop-change';
The render method for the 'MCTContainer' (Multiple Choice Container) class is :
render() {
if (this.state.showRealApp) {
this.props.navigation.navigate('TopicGroupDetails', { topicGroup: this.state.topicGroup });
return null;
} else {
const questions = this.state.questions;
return (
<AppIntroSlider
renderItem={this._renderItem}
slides={questions}
onDone={this._onDone}
onSkip={this._onDone}
activeDotStyle={{ backgroundColor: '#039BE5' }}
showPrevButton
showNextButton
showSkipButton
showDoneButton
renderPrevButton={this._renderPrevButton}
renderNextButton={this._renderNextButton}
renderSkipButton={this._renderSkipButton}
renderDoneButton={this._renderDoneButton}
/>
)
}
}
and its renderDoneButton method is like following :
_renderDoneButton = () => {
return (
<View style={styles.buttonCircle}>
<Icon
name="check"
color="#039BE5"
size={24}
style={{ backgroundColor: 'transparent' }}
/>
</View>
);
};
This is actually the same, exactly the same as I used for the component 'SliderComponent' where I show tutorial introductory text in multiple slides. It renders the 'done' button fine. But the only differenence is that I used package 'react-native-app-intro-slider' there, not 'react-native-app-intro-slider-rerender-on-prop-change'.
Please help me if someone has encountered such a problem. Thanx in advance ...
I finally solved it.
I just added a condition on "renderItem" function of the "AppIntroSlider", that checks whether it is the last slide. If it is it renders a button that calls "AppIntroSlider"-s "_onDone" method that completes the slide show.
_renderItem = ({item}) => {
return(
{
(this.state.isLastSlide) ?
(
<TouchableHighlight
onPress={this._onDone}
>
<View>
<Text>
COMPLETE SLIDE SHOW
</Text>
<Icon
name="check"
color="white"
size={24}
/>
</View>
</TouchableHighlight>
) :
(<View></View>)
}
)
}

useState getting confused when using multiple components

The action taken to update the state in a functional component is incorrectly updating the state of the final component loaded.
I have simplified what I'm doing to very simple code. I'm wondering if I'm missing something in why this does not work
This is parent component that loops to create subcomponents.
{scoreDays.length >0 ? scoreDays.map((el,idx) =>(
<ScoringDay key={idx} date ={el.day} score={el.score}
channels={el.channels} />
)) : null}
This is the ScoringDay component. I'm simply using a button press to update the text in state and have it displayed.
const ScoringDay = props => {
[expanded, setExpanded] = useState(false);
[test, setTest] = useState('starting text');
return(
<View>
<Text>
{test}
</Text>
<Text onPress={() =>setTest('Clicked here')}>Update value</Text>
</View>
)
In my example, 3 "ScoringDay" components are shown on the screen. However, no matter which text "Update value" I click, it always updates the text on the last component.
Why isn't the action being applied to the correct component?? I am using an index on the key...but not sure what else needs to be changed here?
Run the status change from parent to child.
[testtext, setTestText] = useState('starting text');
...
{scoreDays.length >0 ? scoreDays.map((el,idx) =>(
<ScoringDay key={idx} date ={el.day} score={el.score}
channels={el.channels} testtext={testtext} setTestText={setTestText} />
)) : null}
Used
const ScoringDay = props => {
[expanded, setExpanded] = useState(false);
const { testtext, setTestText} = props
return(
<View>
<Text>
{testtext}
</Text>
<Text onPress={() =>setTestText('Clicked here')}>Update value</Text>
</View>
)