So I have Task, and task are created in a new component. They simply add the information to the array that stores all of the tasks.
I tried creating a global boolean called refresh(I know global isn't best practice, I just need to get this working, I don't mind about best practice right now). Then I set extradata = {global.refresh} and I set global.refresh = true every time the page is opened, but it doesn't do anything.
I could really appreciate someone to tell me what i'm doing wrong.
function HomeScreen({ navigation }) {
global.refresh = false;
const [selectedId, setSelectedId] = useState(null);
var currentDate = new Date();
var formatedDate = moment(new Date()).format("YYYY-MM-DD")
console.log(formatedDate)
var one = {
name:'Dishwasher',
task:"Clean",
time:formatedDate}
var two = {
name:'Fishwasher',
task:'<lean',
time:formatedDate
}
global.obj = [
one,
two,
]
return (
<View>
<Button
title="New Task"
accessibilityLabel="Create a new Task"
onPress={() =>
{
navigation.navigate('NewTask')
}}
/>
<FlatList
data = {obj}
extraData={global.refresh}
renderItem={({ item }) =>
<View>
<Task
name={item.name}
description={item.task}
time={item.time}/>
</View>
}/>
<StatusBar style="auto" />
</View>
)
}
In my newTask component, I simply add to the obj array and I set global.refresh = !global.refresh.
class NewTask extends Component {
state = {
item: 'Car',
name:'',
};
updateItem = (item) => {
this.setState({item: item})
}
updateName = (name) => {
this.setState({name: name})
}
render() {
return (
<View>
<Picker
selectedValue={this.state.item}
onValueChange={this.updateItem}>
<Picker.Item label="Car" value="Car" />
<Picker.Item label="HVAC Unit" value="HVAC" />
<Picker.Item label="Fridge" value="Fridge" />
<Picker.Item label="Smoke Detector" value="Smoke" />
<Picker.Item label="Dryer" value="Dryer" />
<Picker.Item label="Water Heater" value="water" />
</Picker>
<TextInput
placeholder = "Name: "
label='Name: '
value={this.state.name}
onChangeText={this.updateName}>
</TextInput>
<Button
title = "Create Task"
onPress={() => {
//Adds data - removed for sake of space.
}
this.props.navigation.navigate('Home')}
}
>
</Button>
</View>
);
}
}
Please if someone could help me that would be amazing. I've been stuck at this for so long. I've tried to read docs but it doesn't make sense to me with it being in two different components. I know I'm supposed to use this.state but I don't get how.
I don't need the best solution, just a solution.
Related
Using react native with typescript and redux toolkit
Hi I'm bothering with render a list of messages via FlatList. By ScrollView everything rendering good but I need to implement infiniti scroll. So I'm doing something like this
const MessagesScreen = () => {
const companyId = useAppSelector(getCompanyId);
const userId = useAppSelector(getUserId);
const {
data: messages,
isLoading,
refetch
} = useGetMessagesQuery({ userId, companyId });
useFocusEffect(refetch);
return (
<FlatList
data={messages}
renderItem={() => {
<Messages messages={messages} />;
}}
/>
);
};
In return() I'm trying to render FlatList with component Messages which is down here:
const Messages = ({ messages }: { messages: Message[] }) => {
const navigation =
useNavigation<RootStackScreenProps<'DrawerNavigator'>['navigation']>();
const { colors } = useTheme();
return (
<View style={styles.container}>
{messages.map(message => {
const createdAt = message.created_at;
const isRead = message.read;
const icon = isRead ? 'email-open-outline' : 'email-outline';
const onClick = () => {
navigation.navigate('Message', {
messageId: message.id
});
};
return (
<TouchableOpacity key={message.id} onPress={onClick}>
<View
style={[styles.message, { borderBottomColor: colors.separator }]}
>
<View style={styles.iconPart}>
<Icon
name={icon}
type="material-community"
style={
isRead
? { color: colors.separator }
: { color: colors.inputFocus }
}
size={24}
></Icon>
</View>
<View style={styles.bodyPart}>
<Text
numberOfLines={1}
style={[isRead ? styles.readSubject : styles.unReadSubject]}
>
{message.subject}
</Text>
<Text
numberOfLines={1}
style={[isRead ? styles.readBody : styles.unReadBody]}
>
{message.body}
</Text>
</View>
<View style={styles.datePart}>
<Text style={{ color: colors.shadow }}>
{dayjs(createdAt).fromNow()}
</Text>
</View>
</View>
</TouchableOpacity>
);
})}
</View>
);
};
Actually behaviour is just rendering white screen with error
Possible Unhandled Promise Rejection (id: 17):
Error: Objects are not valid as a React child (found: object with keys {id, msg_type, created_at, subject, body, author, company_id, read}). If you meant to render a collection of children, use an array instead.
there is problem with your call back function:
you are not returning Messages component
1:Remove curly braces
return (
<FlatList
data={messages}
renderItem={() => <Messages messages={messages}/> }
/>
);
2:Add return statement
return (
<FlatList
data={messages}
renderItem={() => {
return <Messages messages={messages} />;
}}
/>
);
Couple things:
You're using the renderItem callback incorrectly:
<FlatList
data={messages}
renderItem={() => {
// ^ ignoring the renderItem props
return <Messages messages={messages} />;
}}
/>
Here, for each item in the messages array, you're rendering a component and passing all the messages into it. So you'll get repeated elements.
The renderItem callback is passed {item, index} where item is the CURRENT item in the array (index is the index into the array)
See docs here:
https://reactnative.dev/docs/flatlist
The usual thing is the renderItem callback renders ONE item at a time, like this:
<FlatList
data={messages}
renderItem={({item}) => {
return <Message message={item} />;
}}
/>
e.g. I'd make a <Message/> component that renders one item only.
Trying to manage state between parent/child components in React Native. Using functional components and hooks. I want my Collection component (the parent) to be able to update state of an array after calling an onDelete function from the child. I keep tweaking the function but can't seem to get it to work. Thanks in advance!
Parent:
const Collection = () => {
const navigation = useNavigation();
const [showAddGuitar, setShowAddGuitar] = useState(false);
const [gtrlist, setGtrlist] = useState(GUITARS);
const [selectedGuitar, setSelectedGuitar] = useState({});
// Open Details Page
const openDetails = (guitar) => {
setSelectedGuitar(guitar);
navigation.navigate("Details", {
id: guitar.id,
year: guitar.year,
brand: guitar.brand,
model: guitar.model,
sn: guitar.sn,
description: guitar.description,
history: guitar.history,
});
};
// Delete Guitar
const deleteGuitar = (id) => {
setGtrlist(gtrlist.filter((guitar) => guitar.id !== id));
console.log(gtrlist);
};
return (
<View>
{showAddGuitar && (
<GuitarModal onAdd={addGuitar} onCloseModal={onCloseModal} />
)}
{gtrlist.length <= 0 && <Text>Collection empty</Text>}
{gtrlist.length > 0 && (
<FlatList
onDelete={deleteGuitar}
data={gtrlist}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<Text
style={styles.item}
onPress={() => openDetails(item)}
>
{item.year} {item.brand} {item.model}
</Text>
)}
/>
)}
<Button
title="Add New Guitar"
style={[styles.footer, styles.button, styles.buttonOpen]}
onPress={handleAddNewGuitarPress}
/>
</View>
Child:
const DetailsPage = ({ route, navigation, onDelete }) => {
const { id, year, brand, model, sn, description, history } = route.params;
return (
<View>
<Text>
{year} {brand} {model}
</Text>
<Text>S/N: {sn}</Text>
<Text>Description: {description}</Text>
{history &&
history.map((item) => (
<Text key={item.id}>
{item.date} {item.item} {item.cost}
</Text>
))}
<View>
<Button title="Go back" onPress={() => navigation.goBack()} />
<Button
title="Delete guitar"
onPress={() => onDelete(id)}
/>
</View>
</View>
);
};
You can't destructure onDelete from props since you don't pass the onDelete with props. Actually you don't pass it at all. While you passed other details with params, you can also pass onDelete functions with that as well.
const openDetails = (guitar) => {
setSelectedGuitar(guitar);
navigation.navigate("Details", {
id: guitar.id,
year: guitar.year,
brand: guitar.brand,
model: guitar.model,
sn: guitar.sn,
description: guitar.description,
history: guitar.history,
deleteGuitar,
});
};
then of course you need to destructure it on the DetailsPage. But I'm not quite sure what happens after you delete the guitar since you won't have details on that page.
I have rendered a flat list of multiple text inputs and an icon element in a react native component. What I'm trying to achieve is on click of any of the edit icon, I need to focus the correspondent text input. From the past experiences, I know using refs we can achieve this. However, how can I generate dynamic refs based on an index or any other value and focus to the correspondent text input?
const renderItem = ({ item,index }) => {
return (
<View style={styles.item} key={index}>
<View style={styles.data}>
<TextInput
value={item.name}
ref={(input) => {
this.inputRef[item.name] = input;
}}
/>
<TouchableOpacity onPress={this.editName.bind(this, item.name,index)}>
<Icon
name="pencil"
size={20}
/>
</TouchableOpacity>
</View>
</View>
)
}
Function to focus to input:
editName = (name,index) => {
this.inputRef[name].focus();
}
Error: Can't set property 'someName' of undefined. The app is crashing even before the onClick event happens.
Thanks!
I wouldn't get mixed with the bind function.
If you identify your inputs by their name, stick with this when you apply the editName function as well:
function App() {
const names = ['1', '2', '3'];
const inputRef = useRef([]);
// Note this returns a function, that uses the relevant `name` value
const editName = (name) => () => inputRef.current[name].focus();
return (
<div className="App">
{names.map((name) => (
<div key={name}>
<input
type="text"
ref={(input) => {
inputRef.current[name] = input;
}}
/>
<button onClick={editName(name)}>{`Edit ${name}`}</button>
</div>
))}
</div>
);
}
You can check it online as well
so i cannot manage to get the value of any of my inputs, and the only thing is working for me is grabbing them inside render but just getting letter by letter. I want to use the same state for all of them (formData) so when I submit I can grab all the values in an unique object
Any recommendation to solve it??
export default class TimeOff extends Component {
constructor(props) {
super(props);
this.state = {
selectedStartDate: null,
selectedEndDate: null,
selectedValue: '',
formData: '',
};
}
handleSubmit = event => {
event.preventDefault()
console.log(this.state.formData)
render(){
Moment.locale('en')
const { selectedStartDate, selectedEndDate, formData, selectedValue } = this.state;
const minDate = new Date();
const maxDate = new Date(2090, 1, 1);
const startDate = selectedStartDate ? selectedStartDate.toString() : '';
const endDate = selectedEndDate ? selectedEndDate.toString() : '';
return(
<ScrollView showsVerticalScrollIndicator={false}>
<Navbar />
<Text style={styles.hOneTimeOff}>Schedule time off</Text>
<View style={styles.timeOffWrapper}>
<TextInput
name="firstname"
style={styles.timeOffInput}
mode='outlined'
placeholder="First name"
value={formData.firstname}
onChangeText={firstname => this.setState({formData: firstname})}
/>
<TextInput
name="lastname"
style={styles.timeOffInput}
mode='outlined'
placeholder="Last name"
value={formData.lastname}
onChangeText={lastname => this.setState({formData: lastname})}
/>
<Picker
name="role"
style={styles.timeOffPicker}
value={formData.role}
selectedValue={selectedValue}
onValueChange={role => this.handlePicker(role)}
>
<Picker.Item label="What's your role?" value=''/>
<Picker.Item label='Warehouse' value='Warehouse'/>
<Picker.Item label='Bakery' value='Bakery'/>
<Picker.Item label='Dry pack' value='Dry pack'/>
<Picker.Item label='Dry mix' value='Dry mix'/>
<Picker.Item label='Maintenance' value='Maintenance'/>
<Picker.Item label='Mechanical' value='Mechanical'/>
</Picker>
<View style={styles.container}>
<CalendarPicker
startFromMonday={true}
allowRangeSelection={true}
minDate={minDate}
maxDate={maxDate}
selectedDayColor="#00486D"
selectedDayTextColor="#FFFFFF"
/>
<View>
<Text>Start Date: {Moment(startDate).format("M/D/YYYY")}</Text>
<Text>End Date: {Moment(endDate).format("M/D/YYYY")}</Text>
</View>
</View>
<TouchableOpacity style={styles.btnTimeOff} onPress={this.handleSubmit}>
<Text style={styles.btnTextTimeOff}>Submit</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
}
Your on change function is always overwriting your entire previous state.
You can use the setState function with a callback and the spread operator to update the state without losing previous state like so:
First create and function for onTextChange:
const handleChange = (name, value) => {
this.setState(
(previousState) => {
let previousFormData = previousState.formData;
return {
...previousState,
formData: {
...previousFormData,
[name]: value
}
}
}
)
}
Then Update you component like so:
export default class TimeOff extends Component {
constructor(props) {
super(props);
this.state = {
selectedStartDate: null,
selectedEndDate: null,
selectedValue: '',
formData: '',
};
}
handleSubmit = event => {
event.preventDefault()
console.log(this.state.formData)
}
handleChange = (name, value) => {
this.setState(
(previousState) => {
let previousFormData = previousState.formData;
return {
...previousState,
formData: {
...previousFormData,
[name]: value
}
}
}
)
}
render(){
Moment.locale('en')
const { selectedStartDate, selectedEndDate, formData, selectedValue } = this.state;
const minDate = new Date();
const maxDate = new Date(2090, 1, 1);
const startDate = selectedStartDate ? selectedStartDate.toString() : '';
const endDate = selectedEndDate ? selectedEndDate.toString() : '';
return(
<ScrollView showsVerticalScrollIndicator={false}>
<Navbar />
<Text style={styles.hOneTimeOff}>Schedule time off</Text>
<View style={styles.timeOffWrapper}>
<TextInput
name="firstname"
style={styles.timeOffInput}
mode='outlined'
placeholder="First name"
value={formData.firstname}
onChangeText={text => this.handleChange("firstname", text)}
/>
<TextInput
name="lastname"
style={styles.timeOffInput}
mode='outlined'
placeholder="Last name"
value={formData.lastname}
onChangeText={text => this.handleChange("lastname", text)}
/>
<Picker
name="role"
style={styles.timeOffPicker}
value={formData.role}
selectedValue={selectedValue}
onValueChange={role => this.handlePicker(role)}
>
<Picker.Item label="What's your role?" value=''/>
<Picker.Item label='Warehouse' value='Warehouse'/>
<Picker.Item label='Bakery' value='Bakery'/>
<Picker.Item label='Dry pack' value='Dry pack'/>
<Picker.Item label='Dry mix' value='Dry mix'/>
<Picker.Item label='Maintenance' value='Maintenance'/>
<Picker.Item label='Mechanical' value='Mechanical'/>
</Picker>
<View style={styles.container}>
<CalendarPicker
startFromMonday={true}
allowRangeSelection={true}
minDate={minDate}
maxDate={maxDate}
selectedDayColor="#00486D"
selectedDayTextColor="#FFFFFF"
/>
<View>
<Text>Start Date: {Moment(startDate).format("M/D/YYYY")}</Text>
<Text>End Date: {Moment(endDate).format("M/D/YYYY")}</Text>
</View>
</View>
<TouchableOpacity style={styles.btnTimeOff} onPress={this.handleSubmit}>
<Text style={styles.btnTextTimeOff}>Submit</Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
This also preserves the other variables in your state object.
You keep overwriting the "formData" state value.
Since you want it to act as an object (with entries: "firstname", "Lastname", etc) when you setState you should wrap the entries with curly brackets like this:
...
onChangeText={firstname => this.setState({formData: {firstname}})}
...
onChangeText={lastname=> this.setState({formData: {lastname}})}
...
onChangeText={firstname => this.setState({formData: {...this.state.formData, firstname}})}
onChangeText={lastname=> this.setState({formData: {...this.state.formData, lastname}})}
Use this to update the nested state object, it will keep old values and would also update new one
this is my example that I try to check and unchecked the "check-boxes" but I get confused and i will be happy if someone shows me how it should be done.
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { CheckBox } from 'react-native-elements';
const NewPlaceScreen = props => {
const [checked, setChecked] = useState(false);
return (
<View>
<CheckBox
iconRight
right
title="apple"
checked={checked}
onPress={() => setChecked(true)}
/>
<CheckBox
iconRight
right
title="kiwi"
checked={checked}
onPress={() => setChecked(true)}
/>
</View>
);
};
NewPlaceScreen.navigationOptions = {
headerTitle: 'viewsqq'
};
const styles = StyleSheet.create({
TextStyle: {
fontWeight: 'bold',
color: 'grey'
}
});
export default NewPlaceScreen
thats my example above
You need to set them to the opposite of their previous state when pressed. You can do this by using the setState callback:
onPress={() => setChecked(prev => !prev)}
At the moment your check boxes are both using the same state variable checked so they will stay in sync - changing one will change the other. If this is not what you want, you should create a separate state variable for each checkbox.
UPDATE:
To treat each checkbox independently, you need to create state for each checkbox:
const [isAppleChecked, setIsAppleChecked] = useState(false)
const [isKiwiChecked, setIsKiwiChecked] = useState(false)
return (
<View>
<CheckBox
iconRight
right
title="apple"
checked={isAppleChecked}
onPress={() => setIsAppleChecked(prev => !prev)}
/>
<CheckBox
iconRight
right
title="kiwi"
checked={isKiwiChecked}
onPress={() => setIsKiwiChecked(prev => !prev)}
/>
</View>
)
You need to have a separate state for each box, otherwise they will always show the same thing. And you need to set the new state to the opposite of the old state:
const NewPlaceScreen = props => {
const [appleChecked, setAppleChecked] = useState(false);
const [kiwiChecked, setKiwiChecked] = useState(false);
return (
<View>
<CheckBox
iconRight
right
title='apple'
checked={appleChecked} // use the apple-specific state
onPress={() => setAppleChecked(prevState => !prevState)} // use the new apple state function
/>
<CheckBox
iconRight
right
title='kiwi'
checked={kiwiChecked} // use the new kiwi state
onPress={() => setKiwiChecked(prevState => !prevState)} // use the new kiwi function
/>
</View>
);
};