Diet.js
export class Diet extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
};
this.addToList = this.addToList.bind(this);
}
addToList(item) {
const list = [...this.state.list, item];
this.setState({ list });
}
render() {
<FoodCreate addToList={this.addToList} />
return (
<FoodList items={this.state.list} />
)}
FoodCreate
export class FoodCreate extends Component {
constructor(props) {
super(props);
this.state = {
FoodName: "",
calories: 0,
};
}
render() {
return (
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => this.props.addToList(FoodName, calories)}
/>
</Button>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={FoodName}
onChangeText={(FoodName) => this.setState({ FoodName: FoodName })}
/>
<TextInput
placeholder="Calories"
placeholderTextColor="white"
style={styles.inptMacros}
keyboardType="numeric"
value={calories}
maxLength={5}
onChangeText={(calories) => this.setState({ calories: calories })}
/>
FoodList
export class FoodList extends Component {
render() {
return (
<Content>
<List>
<ListItem itemDivider>
<Text>Food</Text>
{this.props.items.map((item, index) => {
return (
<ListItem key={index}>
<Text>{item.FoodName}</Text>
<Text>{item.calories}</Text>
</ListItem>
);
})}
</ListItem>
</List>
</Content>
);
}
}
export default FoodList;
Hi, I'm new to programming and React Native, so I'm trying to create a Grocery List by letting the user type FoodName and Calories and pressing the Icon: Check in FoodCreate page, and List it in the FoodList page, at the moment when I run the code gives me back an error: _this2.props.addToList is not a function, I've tried many solutions but I'm not sure where the error is.
class FoodCreate extends Component {
render() {
return (
<Button title="aaa" onPress={() => this.props.addToList('name')}></Button>
);
}
}
export default class Diet extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
};
this.addToList = this.addToList.bind(this);
}
addToList(item) {
const list = [...this.state.list, item];
this.setState({list});
}
render() {
return <FoodCreate addToList={this.addToList} />;
}
}
I use the above code and didn't get the error
But I think you can have a better code
Don't use this.addToList = this.addToList.bind(this);, you can convert addToList to arrow function and remove this line
addToList = item => {
const list = [...this.state.list, item];
this.setState({list});
};
Related
I just want to clear the state when you move to any other screen in my class component when I'm using navigation v6 (I saw some coments about function component but not class), so I will explain briefly
I did a clean state function in my Body component
class InputBody extends Component {
constructor(props) {
super(props);
this.state = {
fields: JSON.parse(this.props.route).message,
};
}
reset () {
this.setState({});
}
render() {
return (
<Fragment>
{Object.keys(JSON.parse(this.props.route).message).length > 0 ? (
<FieldArraysForm all={JSON.parse(this.props.route).message} resetForm={reset} native={this.props} />
) : (
<ActivityIndicator size="large" color="#eb6b09" />
)}
</Fragment>
);
}
}
And then I call it in my fields array component
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class FieldArraysForm extends Component {
render() {
const {handleSubmit} = this.props.native;
// event listener
const getFields = async (values) => {
return sleep(500).then(() => {
console.log(JSON.stringify(values))
this.props.resetForm();
})
}
return (
<Form>
{this.props.all.map((item) => (
<Field
key={item._id}
name={`customInput.${item._id}`}
component={RenderField}
label={item.field}
type={item.typeFieldAltText}
/>
))}
<View>
<TouchableOpacity onPress={handleSubmit(getFields)}>
<Text>Save Form</Text>
</TouchableOpacity>
</View>
</Form>
);
}
}
Render field function component
class RenderField extends Component {
render() {
return (
<Fragment>
<Texto>{this.props.label}</Texto>
<TextInput
onChangeText={this.props.input.onChange}
{...this.props.input}
keyboardType={this.props.type}
/>
</Fragment>
);
}
}
So how can I call this.prop.reset() on screen change using class component with react navigation v6 ?
DIET (screen)
export class Diet extends Component {
constructor(props) {
super(props);
this.state = {
foodList: [],
};
}
render() {
return (
<View>
<List>
<FlatList
data={this.props.route?.params?.foodList}
keyExtractor={(item, index) => item.key.toString()}
renderItem={(data) => (
<ListItem>
<Button>
<Left>
<Text>{data.item.foodName}</Text>
</Left>
<Right>
<Text>{data.item.calories}</Text>
<Icon name="arrow-forward" />
</Right>
</Button>
</ListItem>
)}
/>
</List>
</View>
FOODCREATE (screen)
export class FoodCreate extends Component {
constructor(props) {
super(props);
this.state = {
food: null,
calories: null,
foodList: [],
};
}
submitFood = (food, calories) => {
this.setState(
{
foodList: [
...this.state.foodList,
{
key: Math.random(),
foodName: food,
calories: calories,
},
],
},
() => {
this.props.navigation.navigate("Diet", {
foodList: this.state.foodList,
});
}
);
};
render() {
return (
<Container>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={this.state.food}
onChangeText={(food) => this.setState({ food })}
/>
<TextInput
placeholder="Calories"
placeholderTextColor="white"
style={styles.inptMacros}
keyboardType="numeric"
value={this.state.calories}
maxLength={5}
onChangeText={(calories) => this.setState({ calories })}
/>
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => {
this.submitFood(this.state.food, this.state.calories);
}}
/>
</Button>
Hello everyone, I'm trying to make an app in which the user has to insert foodName and calories in the FoodCreate screen and once he taps the checkmark it will add the foodName and calories to the Flatlist in the Diet screen (when I launch Expo the first screen to appear is the Diet screen). When I insert the first food item everything goes fine, but when I want to insert another one, the one I inserted before disappears and it shows only the one I just inserted. I don't know if it's a problem related with the Flatlist or React Navigation. But the Flatlist won't keep the items I inserted.
The problem here is the way navigation works,
Everytime you open the FoodCreate screen the the component is mounted again and the FoodList is reset, so the newly added one would be the only item there, you return this as a parameter to Diet screen which will show only one item.
Heres a the better way to do it.
Move the state management to Diet screen
class Diet extends Component {
constructor(props) {
super(props);
this.state = {
foodList: [],
};
}
// Use this to update state.
static getDerivedStateFromProps(props, state) {
if (props.route.params?.food) {
return { foodList: [...state.foodList, props.route.params.food] };
}
return null;
}
And show the value in the state in the flatlist
<FlatList data={this.state.foodList} ...
Change submitFood like below to send only newly created item
submitFood = (food, calories) => {
this.props.navigation.navigate("Diet", {
food: {
key: Math.random(),
foodName: food,
calories: calories,
},
});
}
The easier way is to switch to functional components, you can refer the documentation here
https://reactnavigation.org/docs/params/#passing-params-to-a-previous-screen
I have the following function:
export default function InstitutionInfoScreen({route, navigation}) {
const { title, image, logo, location, phone, email } = route.params;
return (
<ScrollView >
<View style={styles.whiteContainer}>
<InstItem navigation={navigation}/>
</View>
</ScrollView>
);
}
Turns out I want InstItem to get the navigation parameter, because I need it for my SliderEntry component.
InstItem looks like this:
class InstItem extends React.Component {
constructor(props){
super(props);
this.state = {
activeSlide: SLIDER_1_FIRST_ITEM
};
}
_renderItemWithParallax ({item, index}, parallaxProps) {
return (
<SliderEntry
navigate={this.props.navigation}. <-- THIS IS THE ISSUE
type="institution"
data={item}
even={(index + 1) % 2 === 0}
parallax={true}
parallaxProps={parallaxProps}
/>
);
}
render(){
return (
<View style={styles.whiteContainer}>
<Carousel
ref={c => this._slider1Ref = c}
data={DATA2}
renderItem={this._renderItemWithParallax}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
hasParallaxImages={true}
firstItem={SLIDER_1_FIRST_ITEM}
inactiveSlideScale={0.94}
inactiveSlideOpacity={0.7}
// inactiveSlideShift={20}
containerCustomStyle={styles.slider}
contentContainerCustomStyle={styles.sliderContentContainer}
loop={true}
loopClonesPerSide={2}
onSnapToItem={(index) => this.setState({ activeSlide: index }) }
/>
<Pagination
dotsLength={DATA2.length}
activeDotIndex={this.state.activeSlide}
containerStyle={styles.paginationContainer}
dotColor={'rgba(46, 49, 49, 1)'}
dotStyle={styles.paginationDot}
inactiveDotColor={colors.navyblue}
inactiveDotOpacity={0.4}
inactiveDotScale={0.6}
carouselRef={this._slider1Ref}
tappableDots={!!this._slider1Ref}
/>
</View>
);
}
}
However I always get:
typeerror undefined is not an object (evaluating 'this.props.navigation')
I´ve tried to use "navigation" directly instead of "this.props.navigation" but that doesn't work either. How can I fix this?
I want to paas "subjects" array from SubjectsScreen to MarkAttendanceScreen and display the array items as a FlatList.
Parent Component
export default class SubjectsScreen extends Component {
state = {
subjects: ["A", "B"]
};
render() {
return (
...
<MarkAttendanceScreen subjectsArray={this.state.subjects} />
);
}
}
Child Component
export default class MarkAttendanceScreen extends Component {
constructor(props) {
super(props);
this.state = {
subjects: []
};
}
componentDidMount() {
this.setState({ subjects: this.props.subjectsArray });
}
render() {
return (
<FlatList>
{ this.props.subjects.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
}
Using props was giving error when using FlatList with map.
Works fine when extracting value directly from AsyncStorage.
export default class MarkAttendanceScreen extends Component {
state = {
subjects: [],
text: ""
}
componentDidMount() {
Subjects.all(subjects => this.setState({ subjects: subjects || [] }));
}
render() {
return (
<View>
<FlatList
data={ this.state.subjects}
renderItem={({item}) => {
return (
<View>
<Text> { item.text } </Text>
</View>
)
}}
keyExtractor={ (item, index) => index.toString()}
/>
</View>
);
}
}
let Subjects = {
convertToArrayOfObject(subjects, callback) {
return callback(
subjects ? subjects.split("\n").map((subject, i) => ({ key: i, text: subject })) : []
);
},
convertToStringWithSeparators(subjects) {
return subjects.map(subject => subject.text).join("\n");
},
all(callback) {
return AsyncStorage.getItem("SUBJECTS", (err, subjects) =>
this.convertToArrayOfObject(subjects, callback)
);
},
};
this.props.subjects does not exist, but you did set the state in componentDidMount. In the FlatList use this.state.subject.map.
render() {
return (
<FlatList>
{ this.state.subjects.map((item, key)=>(
// ^here
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
You must use the same key name that you used while passing down data to child component e.g. in your case you used key subjectsArray here and You don't need to store this first in state and then use unless you want to update it later.
<MarkAttendanceScreen subjectsArray={this.state.subjects} />
So in your child component, it will be
<FlatList>
{this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>
))}
</FlatList>
D. Smith is correct, you need to change that line to this.state.subjects.map But could also just remove the state variable from the Child Component and use the array directly from props.
export default class MarkAttendanceScreen extends Component {
constructor(props) {
super(props);
}
render() {
return (
<FlatList>
{ this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
</FlatList>
);
}
}
Update:
Flatlists need to be defined like this:
<FlatList
data={ this.props.subjectsArray }
renderItem={({item}) => {
return (
<Text> { item } </Text>)
)
}}
keyExtractor={(item, index) => index}
/>
or you can use it the way you have it and remove the flatlist like:
return this.props.subjectsArray.map((item, key)=>(
<Text key={key}> { item } </Text>)
)}
I'm try to update the state from a function, but I don't find the correct form to bind the scope. My code (I am working with native-base components):
export default class MenuScreen extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
_renderRow() {
return (
<ListItem avatar onPress={() =>
ActionSheet.show(
{options: BUTTONS
}, buttonIndex => { setState({ clicked: BUTTONS[buttonIndex]})}
)}
>
</ListItem>
);
}
render() {
return (
<SectionList
sections={[...]}
renderItem={this._renderRow}
/>
);
}
First option, bind it in constructor
Example
constructor(props) {
super(props);
this.state = {};
this._renderRow = this._renderRow.bind(this);
}
Second option, bind it inline
Example
<SectionList
sections={[...]}
renderItem={this._renderRow.bind(this)}
/>
Third option, use arrow functions
Example
renderRow = () => {
return (
<ListItem avatar onPress={() =>
ActionSheet.show(
{options: BUTTONS
}, buttonIndex => { this.setState({ clicked: BUTTONS[buttonIndex]})}
)}
>
</ListItem>
);
}
My recommendation would be to read this:
https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56
Helps a lot with understanding binding options you have and why one or other might be better in your case.
I suggest to go with binding in constructor:
export default class MenuScreen extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleChange = this.handlePress.bind(this);
}
...
self mental note, "Bind is dummy if I not use the context of the function"
export default class MenuScreen extends React.Component {
constructor(props) {
super(props);
this.state = {};
**this._renderRow = this._renderRow.bind(this);**
}
_renderRow() {
return (
<ListItem avatar onPress={() =>
ActionSheet.show(
{options: BUTTONS
}, buttonIndex => { **this.**setState({ clicked: BUTTONS[buttonIndex]})}
)}
>
</ListItem>
);
}
render() {
return (
<SectionList
sections={[...]}
renderItem={this._renderRow}
/>
);
}