How to access to the prop assigned component in enzyme - material-ui

This is my component:
const Cmp = (props) => (
<List>
<ListItem primaryText='test' leftCheckbox={<Checkbox onCheck={props.onCheck} />} />
</List>
);
I want to test checking of the checkbox. Here is my test:
it('test', (done) => {
const handleCheck = () => {
done();
}
const wrapper = shallow(<Cmp onCheck={handleCheck} />);
wrapper.find('the checkbox').simulate('check');
});
How can I find the checkbox?

You need to find the ListItem element then get the prop and call simulate on it:
it('test', (done) => {
const handleCheck = () => {
done();
}
const wrapper = shallow(<Cmp onCheck={handleCheck} />);
const checkBox = shallow(wrapper.find('ListItem').first().prop('leftCheckbox')())
checkbox.simulate('check');
});

Related

How to stop getting an undefined value when changing a select value using useState and useEffect

I have the following code in my first component, where I fetch some data from which I need to pull an array of strings and send it as values to my Material UI select component:
const Logs = () => {
const [data, setData] = useState({})
const [usersList, setUsersList] = useState([])
const [user, setUser] = useState(null)
const getData = async () => {
try {
const { data } = await axios.request({
method: "GET",
baseURL: 'some url',
url: `/logger`,
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});
setData(data);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
( async () => {
await getData();
})()
}, []);
const isEmpty = obj => Object.keys(obj).length === 0;
useEffect(() => {
if(isEmpty(data) === false) {
setUsersList(data.userNames)
}
console.log('Data', data) //{userNames: ['Admin', 'User1', 'User2']
})
useEffect(() => {
setUser(usersList[0]) //setting the user to Admin
})
const handleUserChange = (event) => {
const value = event.target.value;
setUser(value);
};
return (
<div>
<CssBaseline />
{<LogsSelect
labelId="logs-user-select-label"
selectId="logs-user-select"
handleChange={handleUserChange}
value={user}
list={usersList}
label='User'
/>
}
)
}
My select is defined as a separate (generic) component:
const LogsSelect = (labelId, selectId, handleChange, value, list, label) => {
return (
<FormControl
className={classes.form}
style={{ minWidth: "140px"}}
>
<InputLabel id={labelId}>{label}</InputLabel>
<Select
className={classes.select}
labelId={labelId}
id={selectId}
onChange={handleChange}
value={value}
>
{value && list?.map((el, index) => (
<MenuItem value={el} key={index}>
{el}
</MenuItem>
))}
</Select>
</FormControl>
)}
I get the following error:
Material-UI: You have provided an out-of-range value undefined for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are "". This is happening when rendering the components. I haven't even managed to get my list of values as options in my select, and then try and change the value.
Pass defaultValue="" to the <Select />
...
<Select
defaultValue=""
>
...
</Select>
...

access RTK-query data in createSlice action

what woud be the correct way to access RTK-query data inside createSlice reducers?
Like, for example 'select all' functionality in the code below.
is it possible to access current useGetOrdersQuery() data inside toggleSelectAll() action?
or the only/best way to implement 'select/deselect all' would be to pass useGetOrdersQuery() data to toggleSelectAll() action as action payload?
dashboardSlice.js
const initialState = {
selectedIds: [],
};
export const dashboardSlice = createSlice({
name: 'dashboard',
initialState,
reducers: {
toggleSelectAll: (state, action) => {
//get "useGetOrdersQuery" data id's and assign to state.selectedIds
}
}
});
export const { toggleSelectAll } = dashboardSlice.actions;
export const selectSelectedIds = state => state.dashboard.selectedIds;
orders-lsit.js
import { useGetOrdersQuery } from './api'
import { toggleSelectAll, selectSelectedIds } from './dashboardSlice';
const OrdersList = () => {
const {data} = useGetOrdersQuery(123);
const dispatch = useDispatch();
const selectedIds = useSelector(selectSelectedIds);
return (
<div>
<button onClick={() => dispatch(toggleSelectAll())}>
select/deselect all
</button>
{data.map(o => (
<div>
<h2>{o.name}</h2>
<input
type="checkbox"
checked={selectedIds.includes(o.id)}
/>
</div>
))}
</div>
)
}
api.js
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getOrders: builder.query({
query: (userId) => `${userId}/orders`,
providesTags: [{ type: 'Orders', id: 'LIST' }]
})
})
});
export const { useGetOrdersQuery } = api;
if you just want to save the ids you can do this :
const initialState = {
selectedIds: [],
};
export const dashboardSlice = createSlice({
name: 'dashboard',
initialState,
reducers: {
toggleSelectAll: (state, action) => {
//get "useGetOrdersQuery" data id's and assign to state.selectedIds
const { ids } = action.payload;
state.selectedIds = ids;
return state;
}
}
});
export const { toggleSelectAll } = dashboardSlice.actions;
export const selectSelectedIds = state => state.dashboard.selectedIds;
and
import { useGetOrdersQuery } from './api'
import { toggleSelectAll, selectSelectedIds } from './dashboardSlice';
const OrdersList = () => {
const {data} = useGetOrdersQuery(123);
const dispatch = useDispatch();
const selectedIds = useSelector(selectSelectedIds);
// you can pass the list of ids as a payload to your action and store it for later use.
return (
<div>
<button onClick={() => dispatch(toggleSelectAll({ids: data.map(item => item.id}))}>
select/deselect all
</button>
{data.map(o => (
<div>
<h2>{o.name}</h2>
<input
type="checkbox"
checked={selectedIds.includes(o.id)}
/>
</div>
))}
</div>
)
}
you have something like this too:
check out this link
const store = useStore();
const allDataOfAllQueries = store.getState().api.queries;
console.log(Object.values(allDataOfAllQueries);

How to resolve Error " Too many re-renders. React limits the number of renders to prevent an infinite loop." in react native?

I have develop form to submit complaint by selecting product prom picker. So I externally implement the picker and then use it in complaint submission form. For that I was needed to take selectedValue state into complaint submission form by using function handle change. After i implement that faction and change appropriate places regarding to the tutorial link : [https://github.com/reactjs/reactjs.org/issues/1689][1]
But when I navigate to the complaint submission form it says the error as " Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."So help me to resove and please confirm am I follow the above tutorial correctly.
Complaint submission form:
import * as React from 'react';
import {Button, View, Text, ScrollView, StyleSheet, Alert} from 'react-native';
import {Appbar} from 'react-native-paper';
import {TextInput, HelperText} from 'react-native-paper';
import {useEffect, useState} from 'react';
import AsyncStorage from '#react-native-community/async-storage';
import ProductPicker from './ProductPicker';
const ComplaintSubmission = ({navigation}) => {
const [productID , setproductID] = useState('');
const [description , setdescription] = useState('');
const [token, setToken] = useState('');
useEffect(() => {
saveToken();
}, []);
function handleChange(newValue){
setproductID(newValue);
}
const saveToken = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log('token from storage', token);
setToken(token);
}
const send = () =>{
fetch("http://10.0.2.2:3000/customer/lodge-complaint", {
method: "post",
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`
},
body: JSON.stringify({
description : description,
productID : value
})
})
}
const openAlert = () => {
Alert.alert(
"Complaint Successfully Submitted",
"We review it as soon as possible. Thank you for reaching for us!",
[{
text: "OK",
onPress : () => navigation.navigate("DashboardDrawer" ),
}]
);
}
return (
<ScrollView>
<Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Submit Complaint" />
<Appbar.Action icon="magnify" onPress={() => navigation.openDrawer()} />
</Appbar.Header>
<Text>Plese Fill the following</Text>
<View>
{/*{console.log('renderer token', token)}*/}
<ProductPicker value={productID} onValueChange = {handleChange()} />
<HelperText type="info">
Make sure select the correct Product
</HelperText>
</View>
<TextInput
style={styles.PIDstyle}
label="Description"
onChangeText = {(description) => setdescription(description)}
/>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>This is submittion</Text>
<Button onPress={() => {send(); openAlert();}} title="Submit Complaint" />
</View>
</ScrollView>
);
};
export default ComplaintSubmission;
const styles = StyleSheet.create({
PIDstyle: {
marginTop: 30,
marginLeft: 10,
marginRight: 10,
},
});
And this is the picker component:
import React, {useEffect, useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {Picker} from '#react-native-picker/picker';
import AsyncStorage from '#react-native-community/async-storage';
const ProductPicker = () => {
const [selectedValue, setSelectedValue] = useState('');
const [productDetails, setproductDetails] = useState([]);
console.log('product id---', selectedValue);
useEffect(() => {
getProductList();
}, []);
function handleChange(event){
props.onValueChange(event.target.value);
}
const getProductList = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log(' function eka athule------', token);
fetch('http://10.0.2.2:3000/customer/get-all-products', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`,
},
})
.then((response) => response.json())
.then((json) => setproductDetails(json.data))
.catch((error) => console.error(error));
};
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
handleChange();
}}
>
{productDetails.map((item, index) => {
return (
<Picker.Item label={item.productName} value={props.item.productID} key={index}/>);
})}
</Picker>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: 'center',
},
});
export default ProductPicker;
Why don't you pass the itemValue to handleChange?
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
handleChange(itemValue);
}}
Then change the function:
function handleChange(value){
props.onValueChange(value);
}

React native) where should i fetch data from server more faster?

I'm beginner of react-native. I'm making music platform Application with my team.
I have a question. For example, when i search data in Screen1.
and i want to show related detailed data in Screen2.
but detailed data is stored in my db. so, i should async communication with my server.
But i don't know when i call function is better.
I make getData() function.
shortly example,
In Screen1
<TouchableOpacity onPress={() =>{getData(); navigate('Screen2');}} />
or
In Screen2
useEffect(() => {
getData();
,[]}
My App case is here
(Screen1)
<View>
<FlatList
numColumns ={2}
data={state.data}
keyExtractor={posts => posts.id}
onEndReached={onEndReached}
onEndReachedThreshold={0.8}
ListFooterComponent={loading && <ActivityIndicator />}
renderItem={({item}) =>{
return (
<View style ={{margin:'9%',}}>
<TouchableOpacity onPress={()=>{getCuration({isSong,object:item,id:item.id}); navigate('SelectedCuration', {id: item.id}); }}>
<View style={styles.post}>
<View style={{width:130, height:130}}>
<Imagetake url={item.attributes.artwork.url}></Imagetake>
</View>
<View>
<View>
<Text>{item.attributes.name.split("(feat.")[0]}</Text>
{ item.attributes.name.split("(feat.")[1] ?
<Text>feat. {item.attributes.name.split("(feat.")[1].slice(0,-1)}</Text> :
null}
<Text>{item.attributes.artistName}</Text>
</View>
</View>
</View>
</TouchableOpacity>
</View>
)
}}
/>
</View>
(Screen2)
import React, { useContext, useEffect, useState , Component} from 'react';
import { View, Text, Image, Button, StyleSheet,ActivityIndicator ,TextInput, SafeAreaView, TouchableOpacity, FlatList } from 'react-native';
import { Context as CurationContext } from '../../context/CurationContext';
import { Context as UserContext } from '../../context/UserContext';
import { Context as PlaylistContext } from '../../context/PlaylistContext';
import { Context as DJContext } from '../../context/DJContext';
import {Rating} from 'react-native-elements';
import { navigate } from '../../navigationRef';
import Modal from 'react-native-modal';
const Imagetake = ({url}) => {
url =url.replace('{w}', '500');
url = url.replace('{h}', '500');
return (
<Image style ={{borderRadius :10 , height:'100%', width:'100%'}} source ={{url:url}}/>
);
};
const SelectedCuration = ({navigation}) => {
const {state, postCuration, getmyCuration, deleteCuration, getCurationposts, likecurationpost,unlikecurationpost} = useContext(CurationContext);
const { state: userState, getMyCurating, getOtheruser } = useContext(UserContext);
const { getUserPlaylists } = useContext(PlaylistContext);
const { getSongs } = useContext(DJContext);
const [ispost, setIspost] = useState(false);
const [text, setText] = useState('');
const [showModal, setShowModal] = useState(false);
const [number, setNumber] = useState(0);
const curationid= navigation.getParam('id');
console.log('SelectedCuration Page');
const onClose =() => {
setShowModal(false);
}
return (
<SafeAreaView style={{flex:1}}>
{state.currentCuration.songorartistid == undefined || (state.currentCuration.songorartistid !=curationid) ? <ActivityIndicator/> :
<View style={{flex:1 , backgroundColor:'#fff'}}>
<View style={{flex:2.5}}>
<View style={{flex:2}}>
<View style={{flex:1 , flexDirection:'row'}}>
<View style= {{flex:3, justifyContent: 'center', alignItems:'center'}}>
<View style={{ borderRadius:50,width:100, height:100, backgroundColor:'#666', marginBottom:'10%'}}>
{state.currentCuration.isSong ? <Imagetake url={state.currentCuration.object.attributes.artwork.url}></Imagetake> : null}
</View>
<Text>{state.currentCuration.object.attributes.name}</Text>
</View>
<View style = {{flex:5, marginLeft:'10%',justifyContent:'center', alignItems:'flex-start'}}>
{state.currentCuration.participate.length==0
?
<Text style={{marginBottom:'5%'}}>별점 0</Text>
:
<Text style={{marginBottom:'5%'}}>별점 {(state.currentCuration.rating/state.currentCuration.participate.length).toFixed(2)}</Text>
}
<Text style={{marginBottom:'5%'}}>큐레이션에 참여한사람 {state.currentCuration.participate.length}</Text>
</View>
</View>
</View>
</View>
<View style={{flex:1.7}}>
{state.currentCuration.participate.includes(userState.myInfo._id) ?
<Button
title = "나의 큐레이션 보기"
color ='#E73177'
onPress ={() => {
setShowModal(true);
getmyCuration({id:state.currentCuration.songorartistid})
}}
/>
:
<View style={{flex:1.7}}>
{ispost ?
<View style={{flex:1.7}}>
<View style={{flex:2, justifyContent:'center', marginBottom:'5%', alignItems:'center'}}>
<TextInput
style={styles.inputBox}
value = {text}
onChangeText={text=>setText(text)}
placeholder="큐레이션 내용"
multiline={true}
autoCapitalize='none'
autoCorrect={false}
placeholderTextColor ="#888888"
keyboardType = "email-address"
/>
<Rating
type='heart'
ratingCount={5}
startingValue={0}
imageSize={30}
onFinishRating={(value)=>{setNumber(value);}}
/>
</View>
<View style={{flex:1, flexDirection:'row', justifyContent:'center'}}>
<Button
title = "큐레이션 작성"
color ='#E73177'
onPress ={() => {
postCuration({isSong:state.currentCuration.isSong , rating:number, object:state.currentCuration.object, textcontent:text, id:state.currentCuration.songorartistid})
getMyCurating();
setIspost(false);
setText('');
}}
/>
<Button
title = "큐레이션 취소"
color ='#E73177'
onPress ={() => {
setIspost(false);
setText('');
}}
/>
</View>
</View>
:
<Button
title = "큐레이션 작성하기"
color ='#E73177'
onPress ={() => {
setIspost(true);
}}
/>
}</View>
}
</View>
<View style={{flex:5}}>
<FlatList
data={state.currentCurationpost}
keyExtractor={comment => comment._id}
renderItem={({item}) =>{
return (
<View style={{flex:1}}>
<View style ={{flex:1, flexDirection:'row'}}>
<TouchableOpacity onPress={() => {
if(item.postUserId == userState.myInfo._id){
navigate('Account');
}else{
getUserPlaylists({id:item.postUserId});
getOtheruser({id:item.postUserId});
getSongs({id:item.postUserId});
getCurationposts({id: item.postUserId});
navigate('OtherAccount');
}}}>
<Text>작성자 {item.postuser}</Text>
</TouchableOpacity>
{ item.likes.includes(userState.myInfo._id) ?
<TouchableOpacity style={styles.end} onPress={()=>{ unlikecurationpost({id:item._id, songorartistid:item.songorartistid});}} >
<Text>♥︎</Text>
</TouchableOpacity> :
<TouchableOpacity style={styles.end} onPress={()=>{ likecurationpost({id:item._id, songorartistid:item.songorartistid});}}>
<Text>♡</Text>
</TouchableOpacity> }
<Text style>{item.likes.length}개</Text>
</View>
<View style ={{flex:2, flexDirection:'row'}}>
<Text>{item.textcontent}</Text>
</View>
{showModal ?
<Modal
isVisible={true}
onBackdropPress={onClose}
backdropOpacity={0.1}
style={{justifyContent:'flex-end', margin:0,}}>
<View style={{flex:0.5, backgroundColor:'#fff'}}>
{state.mycurationpost.likes == undefined ?
<ActivityIndicator/> :
<View>
<View>
<Text>작성자 {state.mycurationpost.postuser}</Text>
<Text>{state.mycurationpost.textcontent}</Text>
<Text>좋아요 {state.mycurationpost.likes.length}</Text>
</View>
<View style={{flexDirection:'row', justifyContent:'center'}}>
<Button
title = "큐레이션 지우기"
color ='#E73177'
onPress ={() => {
setShowModal(false);
deleteCuration({id:item._id})
getMyCurating();
}}
/>
</View>
</View>
}
</View>
</Modal>
:null
}
</View>
);
}}
/>
</View>
</View>
}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
inputBox : {
borderWidth:0.8,
borderRadius:5,
borderColor : "#F4D726",
width:'80%',
height: '100%',
},
});
export default SelectedCuration ;
Curation Context is
import AsyncStorage from '#react-native-community/async-storage';
import createDataContext from './createDataContext';
import serverApi from '../api/serverApi';
import { navigate } from '../navigationRef';
const curationReducer = (state, action) => {
switch(action.type) {
case 'get_curation':
return { ...state, currentCuration: action.payload[0], currentCurationpost:action.payload[1] };
case 'get_curationposts':
return { ...state, curationposts:action.payload };
case 'init_curationposts':
return { ...state, curationposts:action.payload };
case 'get_mycuration':
return { ...state, mycurationpost:action.payload };
case 'post_curation':
return { ...state, currentCuration: action.payload[0], currentCurationpost: [...state.currentCurationpost, action.payload[1]] };
case 'like_curationpost':
return { ...state, currentCuration: action.payload[0], currentCurationpost:action.payload[1] };
default:
return state;
}
};
const postCuration = dispatch => {
return async ({ isSong, object, textcontent, id, rating }) => {
try {
const response = await serverApi.post('/curationpost/'+id, { isSong, object, textcontent, rating });
dispatch({ type: 'post_curation', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with postCuration' });
}
}
};
const deleteCuration = dispatch => {
return async ({id}) => {
try {
const response = await serverApi.delete('/curationpost/'+id);
dispatch({ type: 'get_curation', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with deleteCuration' });
}
}
};
const likecurationpost = dispatch => {
return async ({id, songorartistid}) => {
try {
const response = await serverApi.post('/curationpostlike/'+id+'/'+songorartistid);
dispatch({ type: 'like_curationpost', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with likecurationpost' });
}
}
};
const unlikecurationpost = dispatch => {
return async ({id, songorartistid}) => {
try {
const response = await serverApi.delete('/curationpostlike/'+id+'/'+songorartistid);
dispatch({ type: 'like_curationpost', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with unlikecurationpost' });
}
}
};
const initcurationposts = dispatch => {
return async () => {
try {
dispatch({ type: 'init_curationposts', payload: null });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with initcurationposts' });
}
}
};
const getCuration = dispatch => {
return async ({isSong,object,id}) => {
try {
const response = await serverApi.post('/curation/'+id, {isSong,object});
dispatch({ type: 'get_curation', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with getCuration' });
}
}
};
const getCurationposts = dispatch => {
return async ({ id }) => {
try {
const response = await serverApi.get('/curationposts/'+id);
dispatch({ type: 'get_curationposts', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with getCurationposts' });
}
}
};
const getmyCuration = dispatch => {
return async ({ id }) => {
try {
const response = await serverApi.get('/mycurationpost/'+id);
dispatch({ type: 'get_mycuration', payload: response.data });
}
catch(err){
dispatch({ type: 'error', payload: 'Something went wrong with getmyCuration' });
}
}
};
export const { Provider, Context } = createDataContext(
curationReducer,
{ postCuration, deleteCuration, likecurationpost,unlikecurationpost,
initcurationposts, getCuration, getCurationposts, getmyCuration },
{ currentCuration:{}, currentCurationpost:[], mycurationpost:{}, curationposts:null, errorMessage: ''}
)
Curation Routes in Server
const express = require('express');
const mongoose = require('mongoose');
const Curation = mongoose.model('Curation');
const Curationpost = mongoose.model('CurationPost');
const User = mongoose.model('User');
const Notice = mongoose.model('Notice');
const requireAuth = require('../middlewares/requireAuth');
var admin = require('firebase-admin');
const router = express.Router();
require('date-utils');
router.use(requireAuth);
router.get('/user', async(req, res) => {
const user= await User.findOne({email : req.user.email});
res.send(user);
});
//post Curation
router.post('/curationpost/:id', requireAuth, async (req, res) =>{
const { isSong, object, textcontent, rating } = req.body;
var newDate = new Date()
var time = newDate.toFormat('YYYY-MM-DD HH24:MI:SS');
try {
const curationpost = new Curationpost({ isSong, object, rating, postUser: req.user.name, postUserId: req.user._id, time, textcontent, songorartistid:req.params.id });
await curationpost.save();
const curation = await Curation.findOneAndUpdate({ songorartistid:req.params.id }, {$push: { participate:req.user._id }, $inc : { rating:rating }}, {new:true});
res.send([curation, curationpost]);
} catch (err) {
return res.status(422).send(err.message);
}
});
// delete Curation
router.delete('/curationpost/:id', async (req, res) =>{
try {
const curationpost = await Curationpost.findOneAndDelete({_id:req.params.id});
const curationposts = await Curationpost.find({songorartistid:curationpost.songorartistid});
const curation = await Curation.findOneAndUpdate({songorartistid:curationpost.songorartistid},{$pull:{participate:curationpost.postUserId}, $inc:{rating:-1*curationpost.rating}}, {new:true});
res.send([curation, curationposts]);
} catch (err) {
return res.status(422).send(err.message);
}
});
// like Curation post
router.post('/curationpostlike/:id/:songorartistid', requireAuth, async(req,res) =>{
var newDate = new Date()
var noticeTime = newDate.toFormat('YYYY-MM-DD HH24:MI:SS');
try{
const curation = await Curation.findOne({songorartistid:req.params.songorartistid});
const curationpost = await Curationpost.findOneAndUpdate({_id : req.params.id}, {$push : {likes : req.user._id}}, {new:true});
const curationposts = await Curationpost.find({songorartistid:req.params.songorartistid});
if(curationpost.postUserId.toString() != req.user._id.toString()){
try {
const notice = new Notice({ noticinguser:req.user._id, noticieduser:curationpost.postUserId, noticetype:'culike', time: noticeTime, curationpost:curationpost._id });
await notice.save();
} catch (err) {
return res.status(422).send(err.message);
}
}
res.send([curation, curationposts]);
const targetuser = await User.findOne({_id:curationpost.postUserId});
if( targetuser.noticetoken != null && targetuser._id.toString() != req.user._id.toString()){
var message = {
notification : {
title: curation.object.attributes.artistName + ' - ' + curation.object.attributes.name,
body : req.user.name + '님이 큐레이션을 좋아합니다.',
},
token : targetuser.noticetoken
};
try {
await admin.messaging().send(message).then((response)=> {}).catch((error)=>{console.log(error);});
} catch (err) {
return res.status(422).send(err.message);
}
}
}catch(err){
return res.status(422).send(err.message);
}
});
// unlike Curation post
router.delete('/curationpostlike/:id/:songorartistid', requireAuth, async(req,res) =>{
try{
const curation = await Curation.findOne({songorartistid:req.params.songorartistid});
const curationpost = await Curationpost.findOneAndUpdate({_id : req.params.id}, {$pull : {likes : req.user._id}}, {new:true});
const curationposts = await Curationpost.find({songorartistid:req.params.songorartistid});
await Notice.findOneAndDelete({$and: [{ curation:curation._id }, { curationpost:curationpost._id }, { noticinguser:req.user._id }, { noticetype:'culike' }, { noticieduser:curationpost.postUserId }]});
res.send([curation, curationposts]);
}catch(err){
return res.status(422).send(err.message);
}
});
// getCuration
router.post('/curation/:id', async (req, res) =>{
const { isSong, object } = req.body;
try {
const check = await Curation.findOne({songorartistid:req.params.id});
if (check == null){
const curation = new Curation({isSong, object, songorartistid:req.params.id});
await curation.save();
const curationpost = await Curationpost.find({songorartistid:req.params.id});
res.send([curation,curationpost]);
}else {
const curationpost = await Curationpost.find({songorartistid:req.params.id});
res.send([check,curationpost]);
}
} catch (err) {
return res.status(422).send(err.message);
}
});
// get user curationposts
router.get('/curationposts/:id', async (req, res) =>{
try {
const curationpost = await Curationpost.find({postUserId:req.params.id});
res.send(curationpost);
} catch (err) {
return res.status(422).send(err.message);
}
});
// get MyCuration
router.get('/mycurationpost/:id/', requireAuth, async (req, res) =>{
try {
const curationpost = await Curationpost.findOne({$and :[{ songorartistid: req.params.id }, { postUserId: req.user._id }]});
res.send(curationpost);
} catch (err) {
return res.status(422).send(err.message);
}
});
module.exports = router;
So, My app is still waiting when state.currentContent gets.
getCuration() is function to get state.currentContent.
So My Question is
put getData() func in Screen1 when navigate screen2
put getData() func in Screen2 useEffect
problem is getData(in this case getCuration) is Context function(useContext) ??
So, in first solution, when i go to screen2, screen1 is re rendering.
in second solution, getData() is little later than solution 1. So, data is loading longer when people feel i think.
which is better?? help me plz...
There are some solutions that can help you access this state fast
The trick is to make it global and then access them wherever you are in your entire app. You will just make a request from your main screen and check if the state changes or not, so if Screen1 is the first to be displayed in your app, just put this logic inside.
You can
use AsynchStorage: which is asynchronous encrypted but I think a bit complex than
use useGlobal: it's synchronous in term of speediness to access data it's the best one
Use Redux: more complex if your app is not so big
Find the documentation here
https://www.npmjs.com/package/reactn
https://github.com/react-native-async-storage/async-storage
https://code.tutsplus.com/tutorials/using-redux-in-a-react-native-app--cms-36001

React useEffect to obtain Autocomplete options (material UI) from Cloud Firestore

I'm trying to use react useEffect hook to get data from firestore and give it to the options attribute on a Material UI autocomplete select menu.
I have a collection in my firestore called "organisations". That document has an attribute called "shortName".
I'm trying to get the data from the collection and then use it to set the state on a property called orgList, which I can then use in the in the select menu.
This is what I'm trying.
import React, { useState, useEffect } from 'react';
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags() {
const [orgList, setOrgList] = useState();
const [selectedOrgList, setSelectedOrgList] = useState();
useEffect(() => {
firebase
.firestore()
.collection("organisations")
.onSnapshot(snapshot => {
const orgList = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setOrgList(orgList)
})
}, [orgList])
return (
<div>
<Autocomplete
multiple
id="checkboxes-tags-demo"
options={orgList}
disableCloseOnSelect
getOptionLabel={(option) => option.shortName}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.shortName}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params}
variant="outlined"
label="Select Organisation"
placeholder="Acme Inc"
/>
)}
/>
</div>
);
}
The error message I'm getting says:
TypeError: Cannot read property 'shortName' of undefined
NEXT ATTEMPT
Using the suggestion from gdh below, this is the next attempt.
import React, { useState, useEffect } from 'react';
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags() {
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.shortName
}));
console.log(orgList)
setOrgList(orgList);
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, [orgList]);
useEffect(() => {
firebase
.firestore()
.collection("organisations")
.get()
.then((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.shortName
}));
setOrgList(orgList);
});
}, []);
return (
<div>
<Autocomplete
multiple
id="checkboxes-tags-demo"
options={orgList}
disableCloseOnSelect
getOptionLabel={(orgList) => orgList.shortName}
renderOption={(orgList, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{orgList.shortName}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params}
variant="outlined"
label="Select Organisation"
placeholder="Acme Inc."
/>
)}
/>
</div>
);
}
The console log prints both orgList ids, but the shortName is undefined.
I get an error that says:
TypeError: Cannot read property 'toLowerCase' of undefined .
In an attempt to solve this error, I added:
ignoreCase = {false}
to the Autocomplete head tag, but the same error persists. The console logs an error that says:
Warning: React does not recognize the ignoreCase prop on a DOM
element. If you intentionally want it to appear in the DOM as a custom
attribute, spell it as lowercase ignorecase instead. If you
accidentally passed it from a parent component, remove it from the DOM
element
I tried renaming shortName in firestore to 'short' to see if I could avoid the case sensitivity issue, but the same error persists (console logs short as undefined when it has a value in the firestore console).
I know the form is reading from firestore because when I try setting the option as the id of the document, the form loads and the id prints as the value.
you don't have initial values for orgList. Provide a blank array.
you are only registering onSnapshot callback which only executes when organisations collection is changed but you are not fetching any data on mount. This means that your autocomplete will only have values when someone make change in organisation collection. So maintain another useEffect and fetch the data.
refactored code
export default function CheckboxesTags() {
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
useEffect(() => {
firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setOrgList(orgList);
});
}, []);
useEffect(() => {
firebase
.firestore()
.collection("organisations")
.get()
.then((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setOrgList(orgList);
});
}, []);
//.... rest of code...
I found what worked - eventually. There seems to be an issue limiting reading single attribute from the firebase document. In my snapshot, I was trying to read: doc.shortName / doc.short. It needs to be:
shortName: doc.data().shortName,
I didn't end up needing the second .get useEffect as proposed by gdh.
Here is what worked for me:
import React, { useState, useEffect } from 'react';
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import Autocomplete from '#material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags() {
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
// if (doc.exists) {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName
}));
console.log(orgList)
setOrgList(orgList);
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, [orgList]);
// useEffect(() => {
// firebase
// .firestore()
// .collection("organisations")
// .get()
// .then((snapshot) => {
// const orgList = snapshot.docs.map((doc) => ({
// id: doc.id,
// ...doc.data()
// }));
// setOrgList(orgList);
// });
// }, []);
return (
<div>
<Autocomplete
multiple
id="checkboxes-tags-demo"
options={orgList}
disableCloseOnSelect
getOptionLabel={(option) => option.shortName}
renderOption={(orgList, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{orgList.shortName}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params}
variant="outlined"
label="Select Organisation"
placeholder="Acme Inc."
/>
)}
/>
</div>
);
}
As for the toLowerCase error - there is a filterOptions attribute that can be used on Autocomplete. I couldn't figure out how to get that working - but for now, that's a problem for another day.