I made a query to take all the fields of a specific document from Firestore. One of those fields is an array of images, taken from the Firebase Storage. I don't know why, but i can't retrieve those images.
EDIT: I tried to add a abc after the log, before the image, nothing shows.
Here my code:
const [photo, setPhoto] = useState([]);
useEffect(() => {
handlClick();
}, []);
const handlClick = () => {
var i = 0;
setRestaurant([]);
setPhoto([]);
db.doc(idBranch).get().then(data => {
const branchData = data.data();
setRestaurant(branchData);
const ph = branchData.Photo;
ph.forEach(pho =>{
setPhoto(p => [...p, pho]);
})
})
}
return (
{photo && photo.forEach((ph) => {
<View>
<Text> {console.log(ph)}</Text>
<Image source = {{uri : ph}}/>
</View>
})}
)
The console.log is correct (even if they are retrieved more than one times each and shouldn't do that), here the image:
Here the Firestore structure:
Please, help me to understand what I should do to retrieve the images.
Your functional component must return JSX but not the object. Also you must use .map method if you want to render JSX via loop. Try this:
return (
<View>
{photo && photo.map((ph) => (
<View>
<Text>{console.log(ph)}</Text>
<Image source = {{uri : ph}}/>
</View>
))
}
</View>
)
Related
I want to be able to delete elements from my FlatList. I couldn't do it with the onPress of the TouchableOpacity in ItemView so I decided to create a Button with the onPress={botClick} so when I fill the TextInput above that Button it erases that element from the AsyncStorage and then the element is also removed from proddata. My problem is that to see that element removal from the FlatList I have to change my navigation screen to another one and return to see the changes reflected. Can I put something inside of botClick() that refreshes or recharges the screen when the function is called to see the changes automatically without changing screens?
export default function TestScreen () {
const [proddata, setProddata] = useState([]);
const [deletepar, setDeletepar] = useState('');
const whenClick = () => {
console.log("hello");
}
async function botClick(){
try {
await AsyncStorage.removeItem(deletepar);
console.log("Removed");
//Add something here that refreshes or recharges the screen
}
catch(exception) {
}
};
const ItemView = ({item}) => {
return (
<TouchableOpacity onPress={whenClick}>
<View>
<Text>
{item[0]+ ' ' + item[1]}
</Text>
</View>
</TouchableOpacity>
);
};
async function carInState() {
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
setProddata([...proddata, ...result]);
}
useFocusEffect(
React.useCallback(() => {
carInState();
}, [])
);
return (
<View>
<View>
<TextInput placeholder="..." onChangeText={(val) => setDeletepar(val)}/>
<View>
<Button title="Delete" onPress={botClick}/>
</View>
</View>
<FlatList
data={proddata}
renderItem={ItemView}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
};
It isn't a problem you can manipulate the state directly or get all data againg
async function botClick() {
try {
await AsyncStorage.removeItem(deletepar);
// 1) Variant load data again & invoke setProddata
const keys = await AsyncStorage.getAllKeys();
const result = await AsyncStorage.multiGet(keys);
setProddata([...result]);
// 2) Or you can remove it from you list
setProddata(prevProddata => prevProddata.filter(value => value.x.x.x !== deletepar))
} catch (exception) {}
}
How can I promptly update the number state and watch console.log(number) updated value?
const [number,setNumber] = useState(0);
const minus = () => {
setNumber(number-1);
console.log(number);
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
What you are trying to do is a side-effect: print something onto the console.
This is what useEffect hook is for - it lets you perform side effects.
So here is a possible implementation:
function App() {
const [number, setNumber] = useState(0);
const minus = () => {
setNumber(number - 1);
};
useEffect(() => {
console.log(number);
}, [number]);
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
);
}
Of course, it may be an overkill solution if you are just using console.log for debugging purpose. If that's the case, #zynkn and #deepak-k's answers work just fine.
Try this
setNumber((number)=> {number-1 ; console.log(number)});
const [number,setNumber] = useState(0);
const minus = () => {
// setNumber(number-1);
// It is also work well but it is working with async.
// So you can't see the below console.log().
// console.log(number);
setNumber((prevNumber) => {
newNumber = prevNumber - 1;
console.log(newNumber);
return newNumber;
});
}
return (
<>
<div>{number}</div>
<button onClick={minus}>-</button>
</>
)
In my react-native app I have an component array with Image component inside, but i have this error
Warning: Failed prop type: Invalid prop source supplied to
ForwardRef(Image).
file CampaignPreview
render() {
return (
<TouchableOpacity>
<ImageBackground
source={ require(this.props.imageFilePath) }
>
</ImageBackground>
</TouchableOpacity>
);
}
File Home
render() {
let campaigns = [
{
id: 1,
name: "WildFit",
expire: 1868029014,
picFrame: "../assets/picture.jpg",
done: 0.75
}
];
let campaignViews = campaigns.map( c => {
return (
<CampaignPreview
imageFilePath={ c.picFrame }
name={ c.name }
completation={ c.done }
key={ c.id }
/>
);
});
let view = (
<View>
<ScrollView>
{ campaignViews }
</ScrollView>
</View>
);
return view;
}
but If i put string instead of this.props.imageFilePath it works
i seems that 'require(var)' is not possible in react native, i used require('location') forever, you can try save the string in variable.
require only accept literal s string
react native use variable for image file
I have a react component using react-apollo fetching a list. The server is using relay style connection.
My question is when I fetch for next page, it shows the "Loading..." and then the page cursor moves back to the top, shown in the following.
https://imgur.com/a/ImfQPVJ
I want to have a better UX that no "Loading..." text shown, and after fetching, just append the newly fetched result to the back of the list.
This is the code (remove non-relevant code):
class Links extends Component {
fetchNextPage = (pageInfo, fetchMore) => ev => {
const { linksOrder } = this.props;
const after = pageInfo.endCursor;
const queryVars = getQueryVarsFromParam(linksOrder, after);
fetchMore({
variables: queryVars,
updateQuery: (previousResult, { fetchMoreResult }) => {
const { allLinks: { nodes: newLinks, pageInfo: newPageInfo }} = fetchMoreResult;
// Handle no new result
if (newLinks.length === 0) return previousResult;
// With new result, we append to the previous result list
let finalResult = previousResult;
finalResult.allLinks.pageInfo = newPageInfo;
finalResult.allLinks.nodes = finalResult.allLinks.nodes.concat(newLinks);
return finalResult;
}
})
}
render() {
const { linksOrder, classes } = this.props;
const { linkGqlCursorAfter: after } = this.state;
const queryVars = getQueryVarsFromParam(linksOrder, after);
return(<Query query={LINKS_QUERY_GQL} variables={queryVars}>
{({ loading, error, data, fetchMore }) => {
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
const { allLinks: { nodes: links, pageInfo }} = data;
return(
<React.Fragment>
<Grid container spacing={8} className={ classes.linksList } >
{ links.map((link, ind) => (
<Grid item key={link.id}><Link ind={ind} link={link}/></Grid>
)) }
{ pageInfo.hasNextPage ? (
<Grid item key={ "fetchNextPage" } style={{ alignSelf: "center" }}>
<Fab onClick={ this.fetchNextPage(pageInfo, fetchMore) }
size="small" color="secondary"><AddIcon /></Fab>
</Grid>)
: null }
</Grid>
</React.Fragment>
)
} }
</Query>)
}
}
How could I achieve that? Another approach I could think of is not to use <Query> tag at all, and retrieve the Apollo client itself via HOC in onClick hander, fetch the result, and add the result back to the links object by updating the component state.
Then, this begs the question of why we want to use <Query> <Mutation>, when we can always get the Apollo client and handle the query interaction better ourselves?
Since you already have the data you fetched from previous pages in your cache you can render that conditionally in your if (loading) while waiting for new data to be fetched.
if(loading) {
return data.allLinks ?
<>
<LinksComponent {/*pass in the old data here...*/}/>
<Spinner/>
</> : <Spinner/>
}
If you already have data.allLinks you will display that data in a LinksComponent even when new data is being fetched. The Spinner component will be displayed under the LinksComponent while Loading is true. If you don't have any data fetched, you will just display the Spinner component.
I'm trying to render a bunch of pictures in a flatlist. Currently, the pictures do NOT appear when the page is rendered. However, when I click on the screent where the picture should be, it correctly opens a modal, which allows me to view the picture. Why would the picture NOT appear in the flatlist, but in the modal?
First, I get the data I need from the backend
componentWillMount() {
apiService.getAlarmList().then((res) => {
console.log('NotificationHistory:componentWillMount:getAlarmList:res:', res);
for (let i = 0; i < res.length; i++) {
const thisHistory = res[i];
thisHistory.image = null;
thisHistory.idx = i;
this.getItemImage(thisHistory.path, thisHistory.idx);
}
this.setState({ cameraHistory: res });
});
}
this.state.cameraHistory now has an array with all the necessary data.
renderCameraHistory() {
return (<FlatList
data={this.state.cameraHistory}
renderItem={({ item, index }) => {
// console.log('NotificationHistory:renderCameraHistory:item:', item, index);
// console.log('this is rendering the flat list', item.image);
if (item.image !== null) {
return (
this.renderSingleHistory(item, index)
);
}
}}
//Because we're using the indexes(which are ints) we need to convert them to strings first
keyExtractor={(item, index) => index.toString()}
// keyExtractor={(item) => item.path}
//this needs unique keys, however the current array has multiple copies of the same item
//item.key is supposed to work, but still
//extraData={this.state.toggleRefresh}
/>);
}
contains the flatlist component.
Each picture is then rendered via the function below. When the picture is clicked on, a modal is opened and a bigger version of the picture appears, along with a button to close the modal and go back to the list of pictures.
renderSingleHistory(item) {
const dateNTime = item['date-time'];
const dateArr = dateNTime.split(' ');
const dateOnly = dateArr[0];
const timeOnly = dateArr[1];
const logoPic = Config.images.homeIcon;
const picForFlatList = item.image;
return (
<View style={styles.cardView}>
<View style={styles.pictureView}>
<TouchableOpacity
onPress={() => {
this.getBigPicture(item);
}}
>
<View
style={{
width: 100,
height: 100,
}}
>
<Image
source={{ uri: picForFlatList }}
style={{
resizeMode: 'contain'
}}
/>
</View>
</TouchableOpacity>
</View>
<View style={styles.textView}>
<View style={styles.topTextView}>
<Text>{item.serial}</Text>
</View>
<View style={styles.bottomTextView}>
<View style={styles.bottomLeftTextView}>
<Text>{dateOnly}</Text>
</View>
<View style={styles.bottomRightTextView}>
<Text>{timeOnly}</Text>
</View>
</View>
</View>
</View>
);
}
Additional note: if I use a static image as the source, a flatlist of the static image is rendered, HOWEVER, when I click on the image, the picture that's located inside the array, gets loaded into the modal(the picture I want loaded in the flatlist).
The solution was to use ListEmptyComponent inside FlatList. Due to the state not being updated instantly, when the renderItem method is called, the initial object does NOT contain the item.image(2 calls are made to the back, so it takes some time). Therefore, ListEmptyComponent acts as a placeholder until state is completely updated and allows something to be rendered instead of trying to render null.
I was able to discover this via this post