I am learning react native at the moment, I got my random background image to work but there is a bug now and I have no idea how to fix it easily, it now changes background everytime I do anything in app.
Main idea was to change image if app opens I added video what seems to be problem at the moment. Somehow background is not changeing when I update my task.
https://www.youtube.com/watch?v=JkAfe6BDZYg&feature=youtu.be&ab_channel=TheMrMaarek
const { height, width } = Dimensions.get("window");
const randomImages = [
require('./images/talv.jpg'),
require('./images/kevad.jpg'),
require('./images/suvi.jpg'),
require('./images/sügis.jpg'),
];
export default class App extends React.Component {
state = {
newToDo: "",
loadedToDos: false,
toDos: {}
};
componentDidMount = () => {
this._loadedToDos();
};
render() {
const { newToDo, loadedToDos, toDos } = this.state;
console.log(toDos);
if (!loadedToDos) {
return <AppLoading />;
}
return (
<ImageBackground source={randomImages[Math.floor(Math.random()*randomImages.length)]} style={styles.container}>
<StatusBar barStyle="light-content" />
<Text style={styles.title}>MS ToDo App</Text>
<View style={styles.card}>
<TextInput
style={styles.input}
placeholder={"Add new task here..."}
value={newToDo}
onChangeText={this._controlNewToDo}
placeholderTextColor={"black"}
returnKeyType={"done"}
autoCorrect={false}
onSubmitEditing={this._addToDo}
underlineColorAndroid={"transparent"}
/>
<ScrollView contentContainerStyle={styles.toDos}>
{Object.values(toDos)
.sort((a, b) => {
return a.createdAt - b.createdAt;
})
.map(toDo => (
<ToDo
key={toDo.id}
deleteToDo={this._deleteToDo}
uncompleteToDo={this._uncompleteToDo}
completeToDo={this._completeToDo}
updateToDo={this._updateToDo}
{...toDo}
/>
))}
</ScrollView>
</View>
</ImageBackground>
you want to just randomize the image in componentDidMount,
state = {
newToDo: "",
loadedToDos: false,
toDos: {},
currentImage: null,
};
...
componentDidMount = () => {
this._loadedToDos();
this.currentImage = randomImages[Math.floor(Math.random()*randomImages.length)];
};
...
<ImageBackground source={this.currentImage} style={styles.container}>
Related
I am new to React and React Native and I am trying to build an cooking recipe app. I am trying to update my dish state in DishTabNavigator.js from DishIngredients.js so that I can send the data to firebase from DishTabNavigator.js, however I do not know how I can update the state. I tried lifting the state up but I couldn't do it. Been stuck on this for a day now. Any help would be appreciated
DishTabNavigator.js
const Tab = createMaterialTopTabNavigator();
export default function MyTabs({ navigation }) {
const [dish, setDish] = useState([{ ingredients: [] }]);
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity style={styles.iconright}>
<Text>Send dish to Firebase</Text>
<FontAwesome name="save" size={24} color="black" />
</TouchableOpacity>
),
});
});
return (
<Tab.Navigator>
<Tab.Screen
name="Ingredient"
component={DishIngredients}
ingredients={dish}
/>
</Tab.Navigator>
);
}
DishIngredients.js
class DishIngredients extends Component {
constructor(props) {
super(props);
this.state = {
ingredients: [],
textInput: [],
};
//Add TextInput
addTextInput = (index) => {
let textInput = this.state.textInput;
textInput.push(
<TextInput
style={styles.textInput}
onChangeText={(text) => this.addValues(text, index)}
/>
);
this.setState({ textInput });
};
//Function to add values into the states
addValues = (text, index) => {
let dataArray = this.state.ingredients;
let checkBool = false;
if (dataArray.length !== 0) {
dataArray.forEach((element) => {
if (element.index === index) {
element.text = text;
checkBool = true;
}
});
}
if (checkBool) {
this.setState({
ingredients: dataArray,
});
} else {
dataArray.push({ text: text, index: index });
this.setState({
ingredients: dataArray,
});
}
};
//function to console the output
getValues = () => {
console.log("Data", this.state.ingredients);
this.props.ingredients = this.state.ingredients;
console.log(this.props.ingredients);
};
render() {
return (
<View>
<View style={styles.icon}>
<View style={{ margin: 10 }}>
<TouchableOpacity>
<Ionicons
name="add"
size={24}
color="black"
onPress={() => this.addTextInput(this.state.textInput.length)}
/>
</TouchableOpacity>
</View>
<View style={{ margin: 10 }}>
<Button title="Remove" onPress={() => this.removeTextInput()} />
</View>
</View>
{this.state.textInput.map((value) => {
return value;
})}
<Button title="Get Values" onPress={() => this.getValues()} />
</View>
);
}
}
export default DishIngredients;
You could use React Context
const DishContext = React.createContext({
dish: [],
setDish: () => {},
});
export const useDishContext = () => React.useContext(DishContext);
export const AppDishProvider = ({children}) => {
const [dish, setDish] = useState([{ingredients: []}]);
const defaultDish = {
dish,
setDish,
};
return (
<DishContext.Provider value={defaultDish}>{children}</DishContext.Provider>
);
};
Then
/** Wrap your TabNavigator with AppDishProvider */
<AppDishProvider>
<YourTabNavigator />
</AppDishProvider>
Then
/**
* In DishIngredients, or your TabNavigator...
* You get access to dish-state, and setDish using your
* custom-hook `useDishContext`
*/
const { dish, setDish } = useDishContext();
And if you're using class-based component
class DishIngredient extends React.Component {
static contextType = DishContext;
/**
* Current context-value could be accessed by
* `this.context`
*/
render() {
const {dish, setDish} = this.context;
/** .... */
}
}
Assuming you're not having redux wired in your app... ReactContext is your way to go...
Without changing a lot in your current code you can do like this
const addIngredient = (ingredient) => {
// TODO: ingredient
};
return (
<Tab.Navigator>
<Tab.Screen name="Ingredient">
{(props) => (
<DishIngredients
{...props}
ingredients={dish}
addIngredient={addIngredient}
/>
)}
</Tab.Screen>
</Tab.Navigator>
);
As you see there is a function addIngredient (You can rename as you wish) that will be introduced as prop in DishIngredients like this.props.addIngredient(<Your Ingredient text>).
ReactContext also a nice solution above but I think, this will force your app re-render every time your context changes. It might impact on your performance.
I made 2 screens home and editing screen. I want to change values from edit screen without redux and context but I don't know how? and also when I click save in editscreen it's throwing error that undefined is not an object (evaluating '_this.props.navigation.goBack') and displaing blank home screencwhy that's happening. Can some one help me please, below is my code
home.js
class Home extends Component {
state = {
modal: false,
editMode: false.
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
{
key: "2",
title: "John Cena",
des: "As you can see, You can't see me!",
image: require("../assets/cena.jpg"),
},
],
};
addPost = (posts) => {
posts.key = Math.random().toString();
this.setState((prevState) => {
return {
post: [...prevState.post, posts],
modal: false,
};
});
};
onEdit = (data) => {
this.setState({ post: { title: data }, editMode: false });
};
render() {
if (this.state.editMode)
return <EditScreen item={item} onEdit={this.onEdit} />;
return (
<Screen style={styles.screen}>
<Modal visible={this.state.modal} animationType="slide">
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.modalContainer}>
<AddPost addPost={this.addPost} />
</View>
</TouchableWithoutFeedback>
</Modal>
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.setState({ editMode: true })}
style={styles.Edit}
>
<MaterialCommunityIcons
name="playlist-edit"
color="green"
size={35}
/>
</TouchableOpacity>
<Card>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title} numberOfLines={1}>
{item.title}
</Text>
<Text style={styles.subTitle} numberOfLines={2}>
{item.des}
</Text>
</View>
</Card>
</>
)}
/>
</Screen>
Edit.js
import React, { Component } from "react";
import { View, StyleSheet, Image, KeyboardAvoidingView } from "react-native";
import colors from "../config/colors";
import AppButton from "../components/AppButton";
import AppTextInput from "../components/AppTextInput";
class EditScreen extends Component {
render() {
const { item, onEdit, onClose } = this.props;
return (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 100}
>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<AppTextInput value={item.title} />
<AppTextInput value={item.des} />
</View>
<AppButton
text="Save"
onPress={() => {
onEdit(this.state);
}}
/>
</KeyboardAvoidingView>
);
}
}
export default EditScreen;
AppTextInput.js
function AppTextInput({ icon, width = "100%", ...otherProps }) {
return (
<View style={[styles.container, { width }]}>
<TextInput
placeholderTextColor={defaultStyles.colors.medium}
style={defaultStyles.text}
{...otherProps}
/>
</View>
);
}
Try this:
Edit.js
import React, { Component } from 'react';
import { View, StyleSheet, Image, KeyboardAvoidingView } from 'react-native';
import colors from '../config/colors';
import AppButton from '../components/AppButton';
import AppTextInput from '../components/AppTextInput';
class EditScreen extends Component {
constructor(props) {
super(props);
this.state = { ...props.item };
}
render() {
const { onEdit, onClose } = this.props;
const { title, des, image } = this.state;
return (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 100}>
<Image style={styles.image} source={image} />
<View style={styles.detailContainer}>
<AppTextInput
value={title}
onChangeText={text => this.setState({ title: text })}
/>
<AppTextInput
value={des}
onChangeText={text => this.setState({ des: text })}
/>
</View>
<AppButton text="Save" onPress={() => onEdit(this.state)} />
</KeyboardAvoidingView>
);
}
}
export default EditScreen;
onEdit
onEdit = data => {
const newPosts = this.state.post.map(item => {
if(item.key === data.key) return data;
else return item;
})
this.setState({ post: newPosts, editMode: false });
};
Only the direct children of a navigator can access
this.props.navigation
If you want to access that inside the edit screen you can pass it from the Home screen as a prop. Like so:
return <EditScreen item={item} onEdit={this.onEdit} navigation={this.props.navigation} />;
But i don't think you need to go back because you are still on the Home page and just rendering the EditScreen within it. So just changing the state to have editMode: false should be enough
I have a parent component that maps through an array of chapters and renders (an exercise) a child component for every item found and passes an array of exercises to it.
class ExercisesScreen extends Component {
showSelectedItemList = (screenName, text) => {
Navigation.push("ExercisesStack", {
component: {
name: screenName,
options: navOptionsCreator(text)
}
});
};
get chapters() {
return this.props.chapters.map(chapter => (
<TouchableOpacity key={chapter.id}>
<ExercisesList
onPress={() =>
this.showSelectedItemList(chapter.screenName, chapter.name)
}
exercises={chapter.exercises}
/>
</TouchableOpacity>
));
}
render() {
return <View>{this.chapters}</View>;
}
}
const mapStateToProps = state => ({
chapters: chaptersSelector(state)
});
When this child component receives the array of exercises, it maps through it and renders a list of exercises.
class ExercisesList extends Component {
render() {
return this.props.exercises.map(exercise => (
<View key={exercise.id}>
<TouchableOpacity
style={styles.button}
onPress={() =>
this.props.showSelectedItemList(exercise.screenName, exercise.name)
}
>
<Image source={exercise.icon}/>
<View>
<Text>{exercise.name}</Text>
</View>
<Image source={arrow} />
</TouchableOpacity>
<View />
</View>
));
}
}
ExercisesList.propTypes = {
onPress: PropTypes.func,
exercises: PropTypes.arrayOf(PropTypes.object)
};
The result I get from both components rendered simultaneously:
The question is, what should I do in order for them to render themselves separately and show the corresponding ExercisesList for every chapter in ExercisesScreen?
Make your child component ExercisesList as functional component that only show the corresponding ExercisesList for every chapter not perform any rendering.
Like below:
const ExercisesList = (props) => {
const { exercises } = props;
return({
exercises.map(exercise, index) => renderExcercise(exercise, index)
})
}
const renderExcercise = (exercise, index) => {
return(
<View key={exercise.id}>
<TouchableOpacity
style={styles.button}
onPress={() =>
this.props.showSelectedItemList(exercise.screenName, exercise.name)
}
>
<Image source={exercise.icon}/>
<View>
<Text>{exercise.name}</Text>
</View>
<Image source={arrow} />
</TouchableOpacity>
<View />
</View>
)
}
export default ExercisesList;
ExercisesList.propTypes = {
onPress: PropTypes.func,
exercises: PropTypes.arrayOf(PropTypes.object)
};
I have Header component which I would like to use in multiple screens with multiple use cases such as in MainScreen I want to show only profile icon whereas in other screens I would like to use both backButton and profile icon.
I get isProfileIconVisible and isBackButtonIconVisible from props in Header Component.
this.state = {
isProfileIconVisible: props.isProfileIconVisible,
isBackButtonIconVisible: props.isBackButtonIconVisible
}
I have rendering functions.
_renderProfileIcon () {
let profileIcon = (
<View style={styles.profileButtonContainer} >
<CustomIconButton
onPress={this.props.onProfilePress}
></CustomIconButton>
</View>
);
return profileIcon;
};
_renderBackButtonIcon () {
let backButonIcon = (
<View style={styles.backButtonContainer} >
<CustomIconButton
onPress={this.props.onBackPress}
iconName={"arrow-left"}
></CustomIconButton>
</View>
);
return backButonIcon;
};
and in main render function I am making conditional rendering:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon : null}
</View>
)
}
with this setup, I am not able to render either ProfileIcon nor BackButtonIcon.
I got the text prop but not icons.
Header Component propTypes and defaultProps:
Header.propTypes = {
onBackPress: PropTypes.func,
onProfilePress: PropTypes.func,
text: PropTypes.string,
backButtonIconName: PropTypes.string,
isProfileIconVisible: PropTypes.bool,
isBackButtonIconVisible: PropTypes.bool,
};
Header.defaultProps = {
backButtonIconName: 'keyboard-backspace',
isProfileIconVisible: true,
isBackButtonIconVisible: true,
}
And this is how I call Header component from another component:
<Header
text={"Welcome!"}
isProfileIconVisible={true}
isBackButtonIconVisible={false}
onProfilePress={this.handleProfileButtonPress}
style={styles.headerContainer}
/>
Can you help me where I am doing wrong?
Thank you.
Your _renderBackButtonIcon and _renderProfileIcon are functions, you need to call them to get their return values:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}
Note the () after this._renderBackButtonIcon and this._renderProfileIcon.
Side note: There's no reason to have ...otherProps here:
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
You never use it.
There is an argument for adding text to that list and using it, rather than this.props.text within the return value:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, text } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}
I want to pass the props from App.js(parent) to CommentItem.js(child) to Button.js(child of child),
but the props is empty in Button,js(child of child)'s component.
App.js(parent) to CommentItem.js(child)
I pushed mainuser: this.state.head to FlatList, and the props(mainuser) passed by renderItem={({ item }) => <CommentItem {...item}
And, CommentItem.js(child) received mainuser by the code below.
const {
head,
text,
created,
mainuser,
subuser,
} =
CommentItem.js(child) to Button.js(child of child)
I thought props was pased to Button by ,
and Button(child of child) received the props by the code below.
const {
mainuser,
} = this.props;
However, props id empty in Button.js(child of child).
#
Are there any problems of my code?
Could you give some advice please?
App.js(parent)
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
head: [],
list: [],
};
}
_onPress = (text) => {
const list = [].concat(this.state.list);
list.push({
key: Date.now(),
text: text,
done: false,
mainuser: this.state.head,
});
this.setState({
list,
});
}
render() {
const {
head,
list,
} = this.state;
var data = [["User1", "User2", "User3"],];
return (
<View style={styles.container}>
<View style={styles.dropdownHeader}>
<View style={styles.dropdown}>
<DropdownMenu
style={{flex: 1}}
bgColor={'white'}
tintColor={'#666666'}
activityTintColor={'green'}
handler={(selection, row) => this.setState({head: data[selection][row]})}
data={data}
>
</DropdownMenu>
</View>
</View>
<Text>To Do</Text>
<View style={styles.postinput}>
<CommentInput onPress={this._onPress} />
</View>
</View>
<View style={styles.CommentListContainer}>
<FlatList
/*inverted*/
style={styles.CommentList}
data={list}
renderItem={({ item }) => <CommentItem {...item} /> }
/>
</View>
);
}
}
CommentItem.js(child)
const CommentItem = (props) => {
const {
head,
text,
created,
mainuser,
subuser,
} = props;
return (
<View style={styles.container}>
<View style={styles.left}>
<Text style={styles.text}>{text}</Text>
</View>
<View style={styles.function}>
<Button {...mainuser} />
</View>
</View>
);
}
Button.js(child of child)
export default class ApplauseButton extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
const {
mainuser,
} = this.props;
return (
<View style={styles.container}>
<Text>{mainuser}</Text>
</View>
);
}
}
If you want to access mainuser as a prop, then you've to pass it as <Button mainuser={mainuser} />.
As it stands, you're spreading the contents of mainuser.
as per your code it seems that main user is array,
this.state = {
head: [],//array
list: [],
};
....
list.push({
key: Date.now(),
text: text,
done: false,
mainuser: this.state.head,// main user should be array
});
so you need to give prop name also in <Button />
like this
<Button mainuser={mainuser} />