I'm trying to make multiple switches, the problem is that when I enable / disable 1, all others enable / disable together.
useEffect is being used for me to bring my data from my API.
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
await api
.get('/synchronization/group/true')
.then(response => setData(response.data.data))
.catch(error => setData(error));
}
fetchData();
}, []);
useEffect(() => {
// Just return data
}, [data]);
const [isEnabled, setIsEnabled] = useState({
enabled: false
});
const toggleSwitch = () => setIsEnabled((toggle) => !toggle);
const Item = ({ id, title }) => {
return (
<View>
<TextList>{title}</TextList>
<Switch
key={id}
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
);
};
And here my render switches with looping,
It adds component according to the records in the database
const renderItem = ({ item }) => {
const date = new Date(item.synchronization)
const formattedDate = date.toISOString().split('T')[0];
return <Item id={item.id} title={'Sincronização: ' + formattedDate.split('-').reverse().join('/')} />
};
return (
<Container>
<List
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
/>
<TouchableButton onPress={() => { navigation.navigate('Loading') }}>
<TextButton>Carregar para offline</TextButton>
</TouchableButton>
</Container>
);
}
My screen:
Example
If I understand correctly, all the code that you provided is in one component.
You need to store the state of switch button separately.
Try it:
const Item = ({id, title, value, onValueChange}) => (
<View>
<TextList>{title}</TextList>
<Switch
onValueChange={onValueChange}
value={value}
/>
</View>
);
const Component = ({navigation}) => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
await api
.get('/synchronization/group/true')
.then(response => setData(response.data.data))
.catch(error => setData(error));
}
fetchData();
}, []);
useEffect(() => {
// Just return data
}, [data]);
const [state, setState] = useState({
switches: {}
});
const toggleSwitch = useCallback((id) => () => setState((state) => ({
...state,
switches: {
...state.switches,
[id]: !state.switches[id]
}
})), []);
const renderItem = useCallback(({item}) => {
const date = new Date(item.synchronization)
const formattedDate = date.toISOString().split('T')[0];
return <Item id={item.id} title={'Sincronização: ' + formattedDate.split('-').reverse().join('/')} value={!!state.switches[item.id]} onValueChange={toggleSwitch(item.id)}/>
}, [state.switches, toggleSwitch]);
return (
<Container>
<List
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
/>
<TouchableButton onPress={() => navigation.navigate('Loading')}>
<TextButton>Carregar para offline</TextButton>
</TouchableButton>
</Container>
);
};
If you don't need to store states of all switches in your parent component, you can declare state in a component that will be rendered in list.
Here you declare state, that will be handled locally in this component.
const SwitchComponent = (item => {
const [switchState, setSwitchState] = useState(item.state)
return (
<Switch value={switchState} onValueChange={ () => setSwitchState(prevState => !prevState)}/>
)
})
then it can be rendered in List
<View style={styles.container}>
{arr.map(item => (<SwitchComponent item={item}/>))}
</View>
Rendering as many switches as you want, with state handled separately.
You can try it here.
Related
this is my
homePage
as you can see I have some items and a button that I use to add them to the chartScreen, which is chartScreen , I'm using context api to store items in cartContext.js where each item has its own key to make things easier but I cannot correlate the logic for how to assign each item to the button so whenever I press the button its own item gets added to the chartScreen.
const CartContext = createContext();
export function CartProvider({ children }){
const [items, setItems] = useState(
[
{
title: "Pınar",
text: "Pınar Klasik Dana Sucuk (225g)",
pic: <Image
style={styles.image1}
source={require("./Images/klasik_dana_sucuk_paket.png")} />,
key: 1,
},
{
text: "Pınar Uzun Sosis (225g)",
pic: <Image
style={styles.image1}
source={require("./Images/pinar_uzun_sosis.png")} />,
key: 2,
},
]
const [store, setStore] = useState([]);
const addToCart = (text) => {
setStore((prevState) => [
...prevState,
text,
]);
}
return (
<CartContext.Provider value={{items,setItems, addToCart, store}}>
{children}
</CartContext.Provider>
)
I found a cheeky way to send a specific item from addButton.js
const AddButton = () => {
const { items } = useContext(CartContext);
const { addToCart } = useContext(CartContext);
return (
<TouchableOpacity style={styles.button} onPress={() => addToCart(items[0])}>
<Text>+</Text>
</TouchableOpacity>
)
}
and in the chartScreen I view it just like this
const ChartScreen = () => {
const { store } = useContext(CartContext);
if(store.length != 0) {
return (
<FlatList
data={store}
renderItem={({ item }) => (
<View style={styles.listItem}>
<Text style={styles.itemText}>{item.pic} {item.text}</Text>
</View>
)}
/>
)
}
this is for sure not even the wrong way to achieve the goal so can you help me?
You can import your button and make it common for all the items, and pass the value to it.
Here is your button code
const AddButton = ({item}) => {
const { items } = useContext(CartContext);
const { addToCart } = useContext(CartContext);
return (
<TouchableOpacity style={styles.button} onPress={() =>
addToCart(item?.text)}>
<Text>+</Text>
</TouchableOpacity>
)
}
Here you can make button for all you item
const ChartScreen = () => {
const { store } = useContext(CartContext);
if(store.length != 0) {
return (
<FlatList
data={store}
renderItem={({ item }) => (
<View style={styles.listItem}>
<Text style={styles.itemText}>{item.pic} {item.text}</Text>
<AddButton item={item} />
</View>
)}
/>
)
}
I'm trying to append elements to my in react native, using this.state.
But it doesn't return the Text element. My View stays empty...can anyone help?
I'm using this as a reference.
function App() {
const {register, setValue, handleSubmit} = useForm();
this.state = {message: []};
useEffect(() => {
register('author');
register('message');
}, [register]);
var socket = io.connect('http://localhost:3000/');
const onSubmit = data => {
renderMessage(data);
socket.emit('sendMessage', data);
};
socket.on('receivedMessage', function (data) {
renderMessage(data);
});
const renderMessage = data => {
this.state.message.push(data);
};
return (
<View style={styles.sectionContainer}>
<TextInput
style={styles.sectionAuthor}
placeholder="author"
onChangeText={text => setValue('author', text)}
/>
<View style={styles.sectionDescription}>
{this.state.message.map(item => (
<Text>{item.author}</Text>
))}
</View>
<TextInput
style={styles.sectionMessage}
placeholder="Message"
onChangeText={text => setValue('message', text)}
/>
<Button onPress={handleSubmit(onSubmit)} title="Enviar" />
</View>
);
}
You are mixing functional component and class component.
You should use useState to manage state instead of this.state
const [messages,setMessages] = useState([]);
const renderMessage = data => {
setMessages([...messages, data]);
};
then in your rendering, it should use messages.
{messages.map(item => (
<Text>{item.author}</Text>
))}
function App() {
const {register, setValue, handleSubmit} = useForm();
const [message,setMessage] = useState([]);
useEffect(() => {
register('author');
register('message');
}, [register]);
var socket = io.connect('http://localhost:3000/');
const onSubmit = data => {
renderMessage(data);
socket.emit('sendMessage', data);
};
socket.on('receivedMessage', function (data) {
renderMessage(data);
});
const renderMessage = data => {
let new_msg = [...message];
new_msg.push(data);
setMessage(new_msg);
};
return (
<View style={styles.sectionContainer}>
<TextInput
style={styles.sectionAuthor}
placeholder="author"
onChangeText={text => setValue('author', text)}
/>
<View style={styles.sectionDescription}>
{this.state.message.map(item => (
<Text>{item.author}</Text>
))}
</View>
<TextInput
style={styles.sectionMessage}
placeholder="Message"
onChangeText={text => setValue('message', text)}
/>
<Button onPress={handleSubmit(onSubmit)} title="Enviar" />
</View>
);
}
Im trying to make a feature for my app where the user can fill a questionnaire with radio buttons and he can save the progress and can return later to finish it.
Take a look at this code:
const Step3 = () => {
const [questions, setQuestions] = useState([]);
const [answers, setAnswers] = useState([]);
const {addQuest} = useContext(AuthContext);
const getQuestions = async () => {
const locale = 'sq'; // TODO: get current locale
const response = await apiStandarts.get(`/questions?locale=${locale}`, {
params: { _limit: MAX_QUESTIONS, active: 1, _sort:'sortId:ASC' },
});
setQuestions(response.data);
};
const isOptionSelected = (option) => {
const answer = answers[option.question]
if (answer) {
return option.id == answer.id
}
return false;
}
const saveData = () =>{
AsyncStorage.setItem('questions', JSON.stringify(answers)).then(() => {
console.log('data saved', answers)
}).catch((error) => {
console.log(error)
})}
const retrieveData = async () => {
try {
const answers = await AsyncStorage.getItem('questions');
const parsed = JSON.parse(answers);
Alert.alert(`${parsed.id}`);
} catch(error) {
Alert.alert(error)
}
}
useEffect(() => {
getQuestions();
}, []);
const OptionList = (groupOption) => {
return (
<FlatList
data={groupOption.options}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index}) => {
const clickedRadio = () => {
const selectedOption = {[item.question]:{...item}}
setAnswers({...answers, ...selectedOption})
console.log(answers)
}
const status = isOptionSelected(item) ? true : false
return (
<View key={index}>
<Radio initialValue={status} label={item.description} onChange={() => clickedRadio()}/>
</View>
);
}}
/>
);
};
return (
<View style={styles.container}>
<Text style={{ fontWeight: "bold", fontSize: 16, color: "#6B24AA" }}>
{t("Choose an option/Scroll for more questions")}
</Text>
<FlatList
data={questions}
keyExtractor={(result) => result.id.toString()}
renderItem={({ item, index }) => {
return (
<View style={styles.groupOptions} key={index}>
<Text>{item.description}</Text>
<OptionList options={item?.question_options}></OptionList>
</View>
);
}}
/>
<Button onPress={() => addQuest({answers})}>Save progress</Button>
<Button onPress={() => retrieveData()}>Get progress</Button>
</View>
)
}
I've tried with AsyncStorage but i get my questions from an API and the data re-renders every time the app opens again. Any ideas how to achieve this ? Thanks in advance
how can I pass my type2 params on DoctorCategory to another page? I tried like these below, but it gets an error said can't find variable: type2
this type2 is the value from firebase
const Home = ({ navigation }) => {
const [categoryDoctor, setCategoryDoctor] = useState([])
useEffect(() => {
Fire.database()
.ref('category_doctor/')
.once('value')
.then(res => {
console.log('data category: ', res.val())
if (res.val()) {
setCategoryDoctor(res.val())
}
})
.catch(err => {
showError(err.message)
})
}, [])
useEffect(() => {
getData('user').then(res => {
console.log('data user:', res)
})
}, [])
return (
<View style={styles.content}>
<Text style={styles.welcome}>How can we help you?</Text>
<View style={styles.category}>
<DoctorCategory
type1='General'
type2={categoryDoctor.categoryA}
pic={ILLDocGen}
onPress={() => navigation.navigate('ChooseDoctor', type2)} //<== this one
/>
</View>
</View>
)}
And I call it on another page using.
const ChooseDoctor = ({navigation, route}) => {
const type = route.params
return (
<View style={styles.container}>
<Header1 type='light' title= {`Select a ${type.type2}`}/>
</View>
)}
you should use props in ChooseDoctor component! use this:
const ChooseDoctor = ({navigation, route, props}) => {
return (
<View style={styles.container}>
<Header1 type='light' title= {`Select a ${props.type2}`}/>
</View>
)}
The 2nd param is an object
onPress={() => navigation.navigate('ChooseDoctor',{
type2
)}
Reference https://reactnavigation.org/docs/params/#passing-params-to-nested-navigators
I'm trying to display a bunch of data i have on firebase in a flatlist, I don't really know where the problem is right now, i've tried physically filling the array out and that works but it doesn't when i get the data from firebase. I do see that im getting the data on the console log but it's not getting displayed.
function Squad() {
const gk = [];
db.collection('squad').orderBy('position').get().then(snapshot => {
snapshot.forEach(doc => {
const playerObject = doc.data();
gk.push({name: playerObject.name, number: playerObject.number});
console.log(gk);
});
});
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}
You can get the info on mount in an async function, then store it in a stateful array using hooks.
function Squad() {
const [gk, setGk] = useState([]);
const getSquad = async () => {
const ref = db.collection('squad').orderBy('position');
const doc = await ref.get();
const playerObject = doc.data();
const newGk = [...gk, {name: playerObject.name, number: playerObject.number}];
setGk(newGk);
}
useEffect(() => {
getSquad();
}, [])
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}
Start by creating a useEffect hook to act as a componentWillMount function to call the method when the compnent is ready. I have also included catch block in order to show any errors that might occur.
import React, { useState, useEffect } from "react";
const Squad = () => {
const [gk, setGK] = useState([]);
useEffect(() => {
getSquadData();
});
const getSquadData = () => {
db.collection("squad")
.orderBy("position")
.get()
.then((snapshot) => {
let myData = [];
snapshot.forEach((doc) => {
const playerObject = doc.data();
myData.push({
id: playerObject.id,
name: playerObject.name,
number: playerObject.number,
});
});
setGK(myData);
})
.catch((error) => {
console.log("Error getting data: ", error);
});
};
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>
{number} - {name}
</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
);
return (
<View>
<View style={styles.bar}>
<Text style={styles.barText}>goalkeeper</Text>
</View>
<FlatList
data={gk}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
);
};
export default Squad;
So if you are seeing error logs in your console, check your collection in firestore, and also check the rules in your firestore.