React Select with firestore array - reactjs

I'm trying to display a react-select with options equal to Firestore data but I get only one option with all the array. How can I show the options individually? Thanks.
Well, this is my options from react-select:
const options = [{ value: read01, label: read01 }];
and this is where I retrieve the date:
const retrieveNetwork13 = async () => {
try {
//const querySnapshot = await getDocs(collection(db, "cities"));
const q = query(collection(db, "schedule"));
const qq = query(q, where("uid2", "==", uid3));
const querySnapshot = await getDocs(qq);
setRead01(querySnapshot.docs.map((doc) => doc.data().schedule2));
} catch (e) {
alert(e);
}
};

Let's begin by isolating the key parts of your code. Here, you are using a react setter function to set read01 to some transformation of your query snapshot (I'm assuming that elsewhere there's code like const [read01, setRead01] = useState([])):
setRead01(querySnapshot.docs.map((doc) => doc.data().schedule2));
querySnapshot.docs appears to be an array of objects, which you are querying for its data() and then extracting the property schedule2. I can't say what the shape of schedule2 is from the information you've provided, but let's assume that it's just a string. Given all that, you are setting read01 to an array of strings (the result of querySnapshot.docs.map).
Now, let's look at the options you provide to react-select:
const options = [{ value: read01, label: read01 }];
You have specified one and only one option, for which both the value and label will be read01, which we've established is an array of strings. However, value and label in a react-select options array are intended to be strings, not arrays of strings. See e.g. the first example on the react-select homepage. The relevant options array is:
export const colourOptions: readonly ColourOption[] = [
{ value: 'ocean', label: 'Ocean', color: '#00B8D9', isFixed: true },
{ value: 'blue', label: 'Blue', color: '#0052CC', isDisabled: true },
...
];
See how value and label are just plain strings?
I think what you probably intended to do is map read01 into an array of options. Imagine that this is the contents of read01:
const read01 = ["option1", "option2", "option3"];
Then, we could define options as:
const options = read01.map((val) => ({ value: val, label: val }));
Here's a working sandbox for you to play with.

Related

how to make multipe selectable checkboxes using React,GraphQL,Apollo

I'm making multiple selectable checkboxes using React, GraphQL, Apollo.
I would like to make a query if it is selected, insert query and unselected, delete query(I want to generate a query only for selected/removed items)
Right now I'm making it using mutation but I have a problem where everything is deleted and then inserted.
I would like to generate a query only for selected/removed items. How can I fix it?
My code is as follows.
const [updateNoteMutation] = useUpdateNoteMutation();
const updateLogic = (key: string, value: string | number | Condition[]) => {
const noteVariables = {
variables: {
id: noteData.id,
noteAttributes: {
[key]: value,
},
},
};
updateNoteMutation(noteVariables).then(({ errors }) => {});
});
const handleCollection = (name: string, arr: Array) => {
//arr: List of selected checkboxes.
const noteArr = [];
diff.map((val) => {
noteArr.push({ name: val });
});
updateLogic(name, noteArr);
};
updateNoteMutation
mutation UpdateNote($id: ID!, $noteAttributes: FemappNoteInput!) {
femappUpdateNote(input: {id: $id, noteAttributes: $noteAttributes}) {
note {
id
checkboxList {
name
}
}
}
}
Please let me know if there is a source code that I can refer to
Thanks for reading my question.
You can try react-select for that. Really easy to implement and fully customizable.
Here is the repo;
https://github.com/JedWatson/react-select

How to set list of object as default values in material-UI multi select box in reactjs?

I have this example using multi select box from material-ui v4.
The example contains two components the first with a default value list of object that is not working properly and a second component with a list of string that works fine.
The problem: (first component) when I open the Select component the default values is not selected and when I click on the default value it added again to the select box I have the same value multiple time.
Use the same object, eg change.
const [personName, setPersonName] = React.useState([
{ _id: '1', name: 'Oliver Hansen' },
{ _id: '2', name: 'Van Henry' },
]);
to:
const [personName, setPersonName] = React.useState(names.slice(0,2));
I don't remember this being necessary previously with materialUI, but it fixes the problem in your demo, so maybe I just haven't run into it before.
This is what i tried Array.reduce (Shallow copy):
const namesCopy = names.reduce((newArray, element) => {
props.defaultList.map((item) => {
if (item._id === element._id) {
newArray.push(element);
}
});
return newArray;
}, []);
const [personName, setPersonName] = React.useState(namesCopy);

Issue with state update approach for nested objects

Major EDIT
I have quite huge object which is 3 level deep. I use it as a template to generate components on the page and to store the values which later are utilized, eg:
obj =
{
"group": {
"subgroup1": {
"value": {
"type": "c",
"values": []
},
"fields_information": {
"component_type": "table",
"table_headers": [
"label",
"size"
],
}
},
"subgroup2": {
"value": {
"type": "c",
"values": []
},
"fields_information": {
"component_type": "table",
"table_headers": [
"label",
"size"
],
}
},
},
}
Thanks to this I can dynamically generate view which is, as a template, stored in DB.
I'm struggling with 2 things. Firstly, updating values basing on user input for textbox, checkboxes and similar.
I'm doing it this way:
const updateObj = (group, subgroup, value) => {
let tempObj = {...obj}
tempObj[group][subgroup].value.value = value
toggleObj(tempObj)
}
I know that the spread operator is not in fact doing deep copy. However it allows me to work on the object and save it later. Is that an issue? Do I have to cloneDeep or it is just fine? Could cloneDeep impact performance?
Second case is described below
export const ObjectContext = React.createContext({
obj: {},
toggleObj: () => {},
});
export const Parent = (props) => {
const [obj, toggleObj] = useState()
const value = {obj, toggleObj}
return (
<FormCreator />
)
}
const FormCreator = ({ catalog }) => {
const {obj, toggleObj} = React.useContext(ObjectContext)
return (<>
{Object.keys(obj).map((sectionName, sectionIdx) => {
const objFieldsInformation = sectionContent[keyName].fields_information
const objValue = sectionContent[keyName].value
...
if (objFieldsInformation.component_type === 'table') {
return (
<CustomTable
key={keyName + "id"}
label={objFieldsInformation.label}
headers={objFieldsInformation.table_headers}
suggestedValues={[{label: "", size: ""}, {label: "", size: ""}, {label: "", size: ""}]}
values={objValue.values}
sectionName={sectionName}
keyName={keyName}/>
)
}
...
})}
</>)
}
const CustomTable= (props) => {
const { label = "", headers = [], suggestedValues = [], values, readOnly = false, sectionName, keyName } = props
const {obj, toggleObj} = React.useContext(ObjectContext)
//this one WORKS
useEffect(() => {
if (obj[sectionName][keyName].value.type === "complex") {
let temp = {...obj}
temp[sectionName][keyName].value.values = [...suggestedValues]
toggleObj(temp)
}
}, [])
//this one DOES NOT
useEffect(() => {
if (obj[sectionName][keyName].value.type === "c") {
let temp = {...obj, [sectionName]: {...obj[sectionName], [keyName]: {...obj[sectionName][keyName], value: {...obj[sectionName][keyName].value, values: [{label: "", size: ""}, {label: "", size: ""}, {label: "", size: ""}]}}}}
toggleObj(temp)
}
}, [])
return (
//draw the array
)
}
Please refer to CustomTable component.
As on the example Object above, I have 2 CustomTables to be printed. Unfortunately, one useEffect that should work is not working properly. I'm observing, that values field is set only for the last "table" in Obj. When I'm doing shallow copy of obj, it works fine. But I'm afraid of any repercussion that might happens in future.
I'm also totally new to using createContext and maybe somehow it is the issue.
Kudos to anyone understanding that chaos :)
The main issue appears to be that you are not providing your context. What you have is literally passing the blank object and void returning function. Hence why calling it has no actual effect, but mutating the value does.
export const ObjectContext = React.createContext({
obj: {},
toggleObj: () => {},
});
export const Parent = (props) => {
const [obj, toggleObj] = useState({})
const value = {obj, toggleObj}
return (
<ObjectContext.Provider value={value}>
<FormCreator />
</ObjectContext.Provider>
)
}
Ideally you would also make this component above wrap around FormCreator and render it as props.children instead. This is to prevent the entire sub-tree being rerendered every time toggleObj is called. See the first part of this tutorial to get an idea of the typical pattern.
As to the question about mutating state, it absolutely is important to keep state immutable in React - at least, if you are using useState or some kind of reducer. Bugs arising from state mutation come up all the time on Stack Overflow, so often in fact that I recently made a codesandbox which demonstrates some of the more common ones.
I also agree with #SamuliHakoniemi that a deeply nested object like this is actually better suited to the useReducer hook, and might even go one further and suggest that a proper state management library like Redux is needed here. It will allow you to subdivide reducers to target the fragments of state which actually update, which will help with the performance cost of deeply cloning state structure if or when it becomes an actual issue.

React-Hooks: Fetching data results into empty 'hits' array

I'm trying to fetch data with React Hooks. It all seems to work but the hits array is empty even though the data is fetched correctly.
Here's my code:
import React, { useState, useEffect } from 'react';
import MUIDataTable from "mui-datatables";
import { createMuiTheme, MuiThemeProvider, withStyles } from '#material-ui/core/styles';
export default function Dashboard(props) {
var classes = useStyles();
var theme = useTheme();
// local
var [mainChartState, setMainChartState] = useState("monthly");
const [data, setData] = useState({ hits: [] });
const url = my_url;
useEffect(() => {
const fetchData = async () => {
const result = await axios(
url,
);
setData(result.data);
console.log(result.data);
console.log(data);
};
fetchData();
}, []);
return (
<Grid item xs={12}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title="Analyzed DAOs"
data={data.hits}
columns={["Name", "Members", "Proposals", "Voters"]}
options={{
filterType: "checkbox",
}}
/>
</MuiThemeProvider>
</Grid>
)
}
When printing out the result.data, I get an array with 5 objects (as it should be) but when printing out the data.hits the result is am empty array, and the table shows zero rows.
What am I missing? Probably a lifecycle issue, but how do I fix it?
I'm the OP. Looks like for the code, as written in my question, to work, my data needs to be wrapped with a bit of json.
My data, as it comes from the server, is a json array. To make it work I did the following:
var jsonData = {};
jsonData.hits = result.data;
setData(jsonData);
That's it. Now it all works. It's a workaround and there's probably a more elegant solution.
Setting setData({ hits: [result.data] }) will allow your result to be assigned to data.hits.
setData will completely override any default or current value for data.
Actually your code works as expected, I guess you just need to keep the MUIDataTable columns matches the properties and the data structure that returned from your API.
Assume your returned data look like this:
const data = [
{
name: "Joe James",
company: "Test Corp",
city: "Yonkers",
state: "NY"
...
},
// more objects
];
You will need to set your columns like this:
const columns = [
{
name: "name", // This should match the data property
label: "Name", // Label will be shown
options: {...}
},
{
name: "company",
label: "Company",
options: {...}
},
{
// More columns for `city` and `state` etc...
}
Please follow this codeSandbox example.

How to use useState with {}?

given this..
const [question, setQuestion] = useState({})
and question can contain a title and a description.
I.E.
{
title: 'How to use useState with {}?',
decription: 'given this..`const [question, setQuestion] = useState({})`and `question` can contain a title and a description. I.E. { title: 'How to use useState with {}?', decription: '' }How can the title and description be set independently with `setQuestion`?'
}
How can the title and description be set independently with setQuestion?
The setter function you get from useState() expects to be passed argument which will entirely replace the old state. So you can't use it to update just the title, without also passing in all other properties.
But you can derive a new state object from the existing one and then pass that whole object to setQuestion()
setQuestion({
...question,
title: "New title",
})
One thing that I like to do in this situation is to use the React.useReducer hook instead:
function App() {
const initialState = {
title: 'My title',
description: 'My description'
}
const [state, setState] = React.useReducer(
(p, n) => ({ ...p, ...n }),
initialState
)
return (
<div>
<p>{state.title}</p>
<p>{state.description}</p>
<button onClick={() => setState({ description: 'New description' })}>
Set new description
</button>
</div>
)
}
This way, you're only changing the properties that you want and don't have to deal with copying the old state object and creating a new one based on old and new values.
Also, it will probably look more familiar to you if you're just starting with hooks because of the similarity to the this.setState() calls inside class components.
Here's a little example that shows this approach in action:
example
If this is a common pattern you use then I'd suggest writing your own hook to implement the functionality you want.
If it's a one-off thing then you can use the object spread operator to do it fairly cleanly:
setQuestion({
...question,
title: 'updated title',
});
Here is what it would look like pulled out into a separate hook:
const useMergeState = (initial) => {
const [state, setState] = React.useState(initial);
const mergeState = React.useCallback((update) => setState({...state, ...update}));
return [state, mergeState];
};
The official Docs says:
Both putting all state in a single useState call, and having a
useState call per each field can work. However, components tend to be most
readable when you find a balance between these two extremes, and group
related state into a few independent state variables.
Eg:
const [position, setPosition] = useState({ left: 0, top: 0 });
const [size, setSize] = useState({ width: 100, height: 100 });
In case the state logic becomes complex, we recommend managing it with a reducer or a custom Hook.
Also, remember in case you are using any state object in the useState({}) hook, that unlike the classic this.setState, updating a state variable always replaces it instead of merging it, so you need to spread the previous state in order not to lose some of his properties eg: setResource(…resource, name: ‘newName’);
setQuestion({
title: 'YOUR_TITLE',
description: 'YOUR_DESCRIPTION',
id: '13516',
})
State cannot be edited. You need to set new value always. Thus, it can be replaced with spread operator as below.
setQuestion({
...question,
title: 'new title'
})
or
const newQuestion = { ...question, title: 'new title }
setQuestion(newQuestion)

Resources