I have a very basic react-navigation and relay setup. The data is loaded and the console.log outputs the data, but the View and Text are not rendered and I only see a blank screen.
const HomeScreen = () => {
const data = useLazyLoadQuery(QUERY, {});
console.log('data', data);
return (
<View style={{flex: 1}}>
<Text>Data: {data.id}</Text>
</View>
);
}
Related
I'm learning React Native for the first time. I want to implement a function to show/hide the component by touching the screen, not a specific button.
(Please check the attached file for the example image.)
enter image description here
In this code, I've tried to make a function. if I touch the screen (<View style={style.center}>, then show/hide the renderChatGroup() and renderListMessages() included in <View style={style.footer}>. The source code is below.
In my code, it works. However, the two <View> tag is not parallel. the footer view is center View's child.
I want to make them parallel. but I couldn't find the contents about controlling another <View> tag, not a child. In this code, I used setState, then I couldn't control another the below <View>.
Of course, I tried Fragment tag, but it didn't render anything.
How could I do implement this function? Please help me!
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
const { isVisibleFooter } = this.state;
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Firstly I would highly recommend you use react native with functional components and React Hooks as they alternative will soon will be deprecated.
Since onPress is not available on the View Component, you would need to replace it with TouchableWithoutFeedback as you have already done in your code.
For Showing/Hiding a view you would need to use a conditional operator.
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
const { isVisibleFooter } = this.state;
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
{isVisibleFooter && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Here you can see i have replaced
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
with
{isFooterVisible && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
stating that to only display the Footer View when
const isFooterVisible = true;
I'm developing an app in React Native in which I'm trying to display some data provided by a fake API I set up using json server. I'm using the useContext hook to handle the general state of the app and since I'm fairly new to React Native and React in general I need some help handling the response I'm manipulating through the context API.
This is the State file I set up in the context folder
import React, { useReducer } from 'react'
import MenusReducer from './MenusReducer'
import MenusContext from './MenusContext'
import { baseUrl } from '../../shared/baseURL'
const MenusState = (props) => {
const initialState = {
menus: [],
selectedMenu: null
}
const [state, dispatch] = useReducer(MenusReducer, initialState)
const getMenus = async () => {
const response = await fetch(baseUrl + 'RESTAURANTES')
const data = await response.json()
console.log('This is the reducer working'); // This is a test log to see if it works
dispatch({
type: 'GET_MENUS',
payload: data
})
}
const getDetails = async (id) => {
const response = await fetch(`${baseUrl}RESTAURANTES/${id}`)
const data = await response.json()
dispatch({
type: 'GET_DETAILS',
payload: data
})
}
return (
<MenusContext.Provider value={{
menus: state.menus,
selectedMenu: state.selectedMenu,
getMenus,
getDetails
}}>
{props.children}
</MenusContext.Provider>
)
}
export default MenusState;
So here I set up a getMenus() function by which I get all the items I'd like to display in my components. As you can see, I put a test log inside the function to see if it works, which it does.
The problem comes when I try to get those items inside my app components. Here's one of the instances in which I try to get the items to display.
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [response, setResponse] = useState([])
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
setLoading(false);
setResponse(data)
console.log(response);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{response[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}
So inside one of the ScrollViews I'm setting up a test to see if the response can be displayed, which it is not. However, inside the useEffect, I'm setting up a test log with the message 'This is the app executing' which is working, BUT, the response being logged is an empty array.
I'm sure the problem I'm facing has something to do with the asynchronous response between app and server, but I have no clear idea as to how I can address this.
Can someone please point me in the right direction? Thanks in advance!!
Based on your code, I think you can do this
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
data();
setLoading(false);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{menus[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}
I have 2 screens in my App one that has a form where the user stores the data that it fills in AsyncStorage and this screen that reads all the data saved in AsyncStorage and should show the data in a FlatList. The problem here is that nothing is rendered and the screen is blank. I dont know where the problem is located because if you read the code the console.log(productosData) actually returns in my command line exactly the same structure of result. So productosData is loaded without problem but for some reason this doesn't work.
export default function TestScreen () {
const [productosData, setproductosData] = useState([]);
const ItemView = ({item}) => {
return (
<View>
<Text>
{item}
</Text>
</View>
);
};
useEffect( () => {
async function cargarEnEstado() {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
//result = [["a","b"],["c","d"],["e","f"],["g","h"]]
result.forEach(element => (setproductosData(productosData.push(element))));
console.log(productosData);
}
cargarEnEstado()
},[])
return (
<View style={styles.container}>
<FlatList
data={productosData}
renderItem={ItemView}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 2,
justifyContent: 'center',
alignItems: 'center',
}
});
So I maybe thought that the problem was my FlatList and then I decided to take my FlatList out and test the hook with a Text but when I use {productosData} inside the Text the screen shows a number that corresponds with the length of the first array of result. So in this case I see in the screen a 4 because result as well as productosData have this structure [["a","b"],["c","d"],["e","f"],["g","h"]] with the length of the first array being 4.
export default function TestScreen () {
const [productosData, setproductosData] = useState([]);
const ItemView = ({item}) => {
return (
<View>
<Text>
{item}
</Text>
</View>
);
};
useEffect( () => {
async function cargarEnEstado() {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
//result = [["a","b"],["c","d"],["e","f"],["g","h"]]
result.forEach(element => (setproductosData(productosData.push(element))));
console.log(productosData);
}
cargarEnEstado()
},[])
return (
<View style={styles.container}>
<Text> {productosData} </Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 2,
justifyContent: 'center',
alignItems: 'center',
}
});
Any suggestions? Thank you in advance.
The reason nothing is being rendered is because element is an array of strings ['a','b'], if I understood correctly. Try to change your itemView to this
const ItemView = ({item}) => {
return (
<View>
<Text>
{item[0]+ ' , ' + item[1]}
</Text>
</View>
);
};
Also, your useEffect is not very clean. Note that state in React is immutable. By calling push on productosData, you're mutating the state. First, create a shallow copy of your state, then push your new objects into the copy. Only then you would update your state.
However, there is no reason to iterate your results, just spread them on the state, like this:
async function cargarEnEstado() {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
//result = [["a","b"],["c","d"],["e","f"],["g","h"]]
setProductosData([...productosData, ...result])
console.log(productosData); // Also state is async, so this might not log the correct value
}
trying to navigate to a screen after a function is called. The navigation works perfectly well when rendered in the component but not when the function is called and its conditions are met. I've tried passing navigation but that does not work. Why does React Navigation not work directly when outside render()?
onSubmit = () => {
const { base64URI } = this.props
const { captionData } = this.state
if (base64URI !== null && captionData !== null ) {
console.log('post both image data and caption data as type photo')
this.addPhoto(base64URI, captionData);
navigate.navigation('Vault') //navigation not recognised
} else {
console.log('no data')
}
}
render() {
return(
<View style={styles.headerPost}>
<TouchableOpacity style={{position: 'absolute'}} onPress={() => this.props.navigation.goBack()}> // this navigation works..
<Text style={styles.cancelButton}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.shareButton} onPress={() => this.onSubmit()}> //when this function is called and conditions met, I want navigation to happen
<Text style={styles.shareText}>Share</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
You are using navigation in the wrong way.
it should be
this.props.navigation.navigate('Vault')
I have a file here that defines an icon for the title.
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (<HomeHeaderIcon/>)
}
};
HomeHeaderIcon.js
export default class HomeHeaderIcon extends Component {
async componentDidMount(){
const token = await AsyncStorage.getItem('token');
this.setState({token});
}
state={
token:null
};
render() {
return (
<View>
{
this.state.token ===null
?
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("LogStack"))}>
<Icon name="ios-power" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>
)
:
(
<TouchableOpacity
onPress={() => (NavigationService.navigate("Profile"))}>
<Icon name="ios-home" size={30} style={{color: "white",marginRight:wp("5%")}}/>
</TouchableOpacity>)
}
</View>
);
}
}
The system works exactly as I want. If there is a token, I say icon1 or icon2 show. The problem is I do this in componentDidMount, the icon does not change without refreshing the page. How do I render it again?
componentDidMount is called, as the name suggests, just once, when the component is mounted. Use componentDidUpdate to decide how your component behaves based on what piece of props or state has changed.
Read the documentation for more information regarding lifecycle methods.