react using .map() inside component prop attribute - reactjs

I'm trying to conditionally render a component based on if that user is online. The problem im experiencing is with the status prop on my MessengerFriend component. In my conditional statement it is returning both 'online' and 'offline', but when I only use the equivalent if It returns the online status correctly. My initial thought was that using onlineFriends.map() is returning both statuses because there are in fact users both online and offline.
const [friendsList, setFriendsList] = useState([])
const [onlineFriends, setOnlineFriends] = useState([])
let mappedFriendChatList = friendsList.map(friend => {
return(
<MessengerFriend
key={friend.friend_id}
value={friend}
status={onlineFriends.map(onlineFriend => onlineFriend.userId == friend.user_id ? 'online' : 'offline')}
/>
)
})
I'm expecting to have a list of all my friends and there status 'online' and 'offline' to display correctly

Map is the wrong method for this use-case; some is what you need.
{onlineFriends.some(onlineFriend => onlineFriend.userId === friend.user_id) ? 'online' : 'offline'}
You could also use filter with a length check on the result array, or find (which would work identically to some in this case).

Related

React query refetch even if the key already exist in cache

I built a form using react,react-query, material ui, formik and yup (for schema validation).
link to the code
I built custom fields:
SelectField
AutocompleteField
CacheAutocompleteField - cache field using react-query
queryAsyncFunc props - get async function and cache the data using react-query
I have 3 fields:
Type - Select field
Country - CacheAutocompleteField
City - CacheAutocompleteField
My scenario:
I select any type from my hardcoded list (Type A, Type B , Type C),
I search any country, then I search any city
What I'm trying to do?
every time I select a new type (from type options) - I want the country and city fields to be reset.
every time I already search the same key (the queryKey is combined of cacheKey+inputValue) , it will not call to api, it will get the results from cache (that's what I chose to use from react-query to do).
What I'm getting when I run my code?
When I select a type A, enter a country “Island” it will fetch data from api and get the data.
Then when I select a type B, enter a country “Island” - It will fetch data from api and get the data.
But when I select a type A and and same country “Island” again - I don’t want it to fetch data from api - I want it to get data from cache (that’s the reason I chose to work with react-query too) because it already search for this data with the same type. The queryKey is depended of other type field.
when I search anything from autocomplete and it not find it, then I try to reset it by select any other type, it will kind of reset the value of the input but it still exist in inputValue of the country.
for example I select type C, then enter "lakd" in country, then select any other type, it not reset it. reset works for me only when anything find in autocomplete and I select it. I guess it's because the autocomplete component not have inputValue props, but when I use it it make me other issues.
For your first issue, I think it would be better to use enable property of react-query(to handle it with debounceInputValue).
so you can use boolean state for enable and disable it and in useEffect for debounceInputValue make it true but when data arrived make it false again
sth like this :
const [queryStatus, setQueryStatus] = useState(false);
const { data, isFetching, refetch } = useQueryData(
cacheKey,
inputValue,
queryAsyncFunc,
queryStatus
);
useEffect(() => {
if (debounceInputValue.length > 1) {
setQueryStatus(true);
}
}, [debounceInputValue]);
useEffect(() => {
setDataOptions(data ? [...data] : []);
setQueryStatus(false);
}, [inputValue, data]);
and in useQuery:
enabled: enabled,
staleTime: Infinity,
And for your second issue, I fixed it by setting state for inputValue and onChange for it
like this :
const onInputChange = (_,newInputValue)=>{
setInputValue(newInputValue);
}
and in autoComplete :
inputValue={inputValue}
onInputChange={onInputChange}
Codesandbox
You needn't call refetch. It call the API regardless of the cache.
Comment/Remove this code
// useEffect(() => {
// if (debounceInputValue.length > 1) {
// refetch();
// }
// }, [debounceInputValue, refetch]);
And you should enable the useQuery
enabled: true,
And use debounceInputValue instead of inputValue for useQueryData
https://codesandbox.io/s/react-query-autocomplete-forked-d84rf4?file=/src/components/FormFields/CacheAutocompleteField.tsx:1255-1263

s.indexOf is not a function at Function.n.fromString

I have no idea why I'm getting this error or from where is coming from because I think I'm not using that ?
I'm doing a firebase update after an user update a row from DataGrid MUI and I'm doing the update as I would normally do nothing different at all and it just jumps into that error.
I'm not sure if is an React error, JS error, Firebase error, MUI error. but I THINK is a firebase error because the path says so
This is what I was trying to do:
const [editRowsModel, setEditRowsModel] = React.useState({});
const [editRowData, setEditRowData] = React.useState({});
const handleEditRowsModelChange = React.useCallback(
(model) => {
const editedIds = Object.keys(model);
if (editedIds.length === 0) {
console.log(editRowData)
console.log(editRowData.uid)
console.log(editRowData.nombre)
console.log(editRowData.colegio)
console.log(editRowData.grado)
const temporalQuery = db.collection("usuarios").doc(user.uid).collection("estudiantes").doc(editRowData.uid);
temporalQuery.update({
nombre: editRowData.nombre,
colegio: editRowData.colegio,
grado: editRowData.grado
})
} else {
setEditRowData(model[editedIds[0]]);
}
setEditRowsModel(model);
},
[editRowData]
);
This is what the console.log shows up. I honestly don't see any error in the way I code it that's how I always do it, never had an issue before. First time I update from a nested collection though
This is how it looks in the firebase
And yes the user.uid also comes correctly
Found the issue is because the data comes with a whole string of information instead of just the value as I though it was coming.
All I had to do was to call the field + the value when I was using it.
Ej:
if I wanted the uid I had to call it editRowData.uid.value
the .doc(name) only accepts strings, so check if your user.uid is a string

Handling unknown number of states in React

I am working on a React Native project and I have a React component which displays a list of questions and their options. Let's say all questions are multiple choices, and I use a Picker for that, DropDownPicker. (react-native-dropdown-picker). I need a way to manage whether the picker is opened or closed. The component has 2 props, open and setOpen. There is an unknown number of questions, so I can't initialise all the states.
I've tried doing const [open, setOpen] = useState([]), where open is an array of open statuses. Here's a code snippet:
questions.map((v,i) => {
setOpen([...open, false]); // causes infinite renders
return (
...
<DropDownPicker
open={open[i]}
setOpen={o => { // returns true/false
let list = [...open];
list[i] = o;
return list;
}/>
...
)
}
The code runs into infinite renders when appending a boolean to the state at setOpen([...open, false]). Another way I've tried is to use a variable rather than a state variable, but that doesn't work either.
Can I check whether there's an ideal way to handle an unknown number of states?
Your approach of using a state that is an array of Booleans seems like a reasonable approach. I would do it something like this:
// Initialize to array of false, of length equal to questions
const [open, setOpen] = useState(questions.map((v) => false));
return questions.map((v,i) =>
<DropDownPicker open={open[i]} setOpen={(o) => {
let list = [...open];
list[i] = o;
setOpen(list);
}/>
);
Note that setOpen does not immediately change open; rather, it schedules a change to the open state which happens after the render completes. This is why your code doesn't converge; every render triggers open to grow more.
You can manage open and close status in question array for example
let question:any[] = [
{id:1, questionText:'Some text',open:false, answers:[]}
{id:2, questionText:'Some text',open:false, answers:[]}
]
{question.forEach((item)=>{
<DropDownPicker
open={open[i]}
setOpen={o => { // returns true/false
let index = question.findIndex(id)=>id==itemid;
question[index].open = true;
}
}/>
});
}

How to get a reference to a DOM element that depends on an input parameter?

Say I have the standard TODO app, and want a ref to the last item in the list. I might have something like:
const TODO = ({items}) => {
const lastItemRef = Reeact.useRef()
return {
<>
{items.map(item => <Item ref={item == items.last() ? lastItemRef : undefined} />)}
<>
}
}
But this doesn't seem to work - after lastItemRef is initialized, it is never subsequently updated as items are added to items. Is there a clean way of doing this without using a selector?
I think in your case it depends upon how the items list is updated. This is because useRef won't re-render the component if you change its current attribute (persistent). But it does re-render when you choose, for example, useState.
Just as a working case, see if this is what you were looking for.
Ps: check the console

Infinite loop when set with React Hooks

there is such a production in react. I want the user to delete posts that they have shared. I do this:
When the user logs in, I keep the user id as a cookie.
I get the user name of the user that is equivalent to the id in the database with the captured id - HaoAsk
I check with the username of the user who shares the post with the username I received
if it is equal, I allow the user with setOwner.
But it goes into an endless loop.
const user = response.data.filter((dataItem) => (dataItem._id === userid));
const userNickname = JSON.stringify(user.map((value) => { return value.nickName }))
const nickName = userNickname.slice(2, -2)
const deneme1 = posts.filter( (data) => (data.who === nickName))
deneme1 ? setOwner([{ who: nickName, status: true}]) : setOwner([{ status: false }])
console.log(owner)
When I use the following code, everything I write with console.log enters an infinite loop. I couldn't figure it out.
deneme1 ? setOwner ([{who: nickName, status: true}]): setOwner ([{status: false}])
Thanks in advance, Yours!
For any functional component, you normally want to make sure you don't use set outside the event function.
In your case,
const onClick = e => {
// safe to use set method
setOwner()
}
return (
<Component onClick={onClick} user={user} />
)
Because everything inside a functional component is inside the render cycle. It'll run when react try to refresh the display. In your case you set a state variable which triggers the render and then set the variable and then the infinite circle :)
What this means is that you have to find out an event after you initialize your component. In your case, it's a bit tricky, because you want to call this event right away automatically for you.
Please refer to something like this, How to call loading function with React useEffect only once

Resources