I'm trying to make a table with react-table inside a react component, but instead I get hook error.
export const RepresentativesTable = () => {
const data = useMemo(() => [
{
id: 1,
price: 20,
},
{
id: 2,
price: 34,
},
{
id: 3,
price: 12,
},
]);
const columns = useMemo(() => [
{
Header: "IDs",
accessor: "id",
},
{
Header: "Prices",
accessor: "price",
},
]);
console.log(columns);
console.log(data);
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({
columns,
data,
});
//later I return some table HTML structure
}
And while running this code I get the following error at call of useTable({columns,data})
"react.development.js:209 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."
None of the 3 suggestions seemingly match my code as I checked react version and there are no multiple copies of react.
Is this a problem with formatting data and columns and if so what error (I think I follow the tutorials and documentation well) and how to fix it?
Related
I am developing a component where I will get the data from a call back function. Initially the state of the component will be empty [], later once the callback function is called I need to update the values into the state. At a time I'll recive only one array, meaning user can add one item at a time that item will consists of nested objects and array values. I have added the logic for the same to handle the scenario, but when I am testing in jest when I am trying to add another set of item from mock meaning the user can select next item when the done with selecting and submitting the first item at that time my logic is getting failed, I am not getting where I went wrong, could any one help me to resolve this issue, thanks in advance! I have added the mock data structure and logic and jest test below.
Mock:
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
}
ItemID:'123'
}
Code:
class ItemComp extends React.Component{
this.state = {
processingItems:[]
onAddItemHandle = (processingItem) => {
this.setState(prevState => ({
processingItems: [...prevState.processingItems, processingItem]
}))
}
JEST:
describe('handleonAddItem', () => {
it('should allow to add multiple items based on prevState', () => {
const compView = mountWithIntl(
<compView
itemId={12}
/>
}
const instance = compView.find(compViewComponent).instance();
instance.onAddItemHandle(items) // when I am giving only one instance my logic is working
instance.onAddItemHandle(items) //when I am giving it for second time it's failing I am getting error like expected - 0 , received +18 I want to update the items here when user clicks for second time but it is failing.
expect(instance.state.processingItems).toEqual([items])
Missing a ',' before the ItemID is the only issue I faced while reproducing.- https://codesandbox.io/s/intelligent-chaplygin-0ot56e?file=/src/App.js
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
},
ItemID:'123'
}
I've got my <MockedProvider /> set up passing in mocks={mocks}. everything is working, all good.
the issue is I have a form that whenever any part of it is edited then this makes a mutation, which returns a response and updates the total. say for example, quantity is changed, mutation increases quantity from 1 to 2. total price should double
problem is that in unit tests and mocked provider you only test the functionality in props and hardcoded response. it's not a proper test. perhaps it's more of an e2e/integration test but I was wondering if there's anything you can do with MockedProvider that allows for better testing in this situation?
Instead of using the normal static result property of the objects in the mocks array, you can set a newData function that will run dynamically and use whatever is returned as the result value. For example:
let totalNoteCount = 0;
const mocks = [{
request: {
query: CREATE_NOTE,
variables: {
title: 'Aloha!',
content: 'This is a note ...',
},
},
newData: () => {
// do something dynamic before returning your data ...
totalNoteCount += 1;
return {
data: {
createNote: {
id: 1,
totalNoteCount,
},
},
};
}
}];
I am building an app with vue.js and vuex that stores entries of household accounts (i.e. personal expenses and incomes). The entries are filtered by month; a slider can be used to change months. The current code is on Github.
Displaying existing entries works fine. The problem arises when I try to add a new entry. When I add an entry for an existing month, it works fine. But when I add an entry for a new month (i.e. a month for which no other entry exists yet), I get a myterious warning and a myterious error: Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node. Note: When I say month I mean month-year combination.
Any ideas where this error might be coming from?
Here is some more info about the current app. The current application looks like this:
My store looks like this:
const store = createStore({
state() {
return {
categories: [
{
type: "expense",
key: "living",
value: "Wohnen",
},
...
],
entries: [
{
id: 1,
date: "2020-10-15T14:48:00.000Z",
type: "expense",
category: "Lebensmittel",
amount: 15.24,
note: "Edeka",
},
...
],
};
},
getters: {
sorted_entries(state) {
// Returns all entries, sorted by date
...
return sorted_entries;
},
categories(state) {
return state.categories;
},
entriesMonths(state) {
// Return an ordered arrays of all months present among entries
// Ex.:{index: 0, month: "11", year: "2019", displayMonth: "November 2019"}
...
return entriesMonths;
},
},
mutations: {
addEntry(state, entryData) {
const newEntry = {
id: new Date().toISOString(),
date: entryData.date,
type: entryData.type,
category: entryData.category,
amount: entryData.amount,
note: entryData.note,
};
console.log("right before push")
state.entries.push(newEntry);
},
},
actions: {
addEntry(context, entryData) {
context.commit("addEntry", entryData); // Place to store in backend server
},
},
});
In EntryList.vue I get sorted_entries and entriesMonths from the store:
sorted_entries is used to calculate filtered_entries which filters the entries according to what month is currently displayed by the slider (initialization: latest month among entries in store). filtered_entries is displayed as a list
entriesMonths is passed to another component MonthSelector.vue which implements the slider to change months and emits the new month after a change to EntryList.vue so it can update filtered_entries.
New entries are added via NewEntryForm.vue. This component includes a form to ask the user for the new entry data. It then emits this data as an object to its parent AddEntry.vue which in turn sends it to the store. There an action triggers a mutation which adds the entry to the state.
Of cause there are a lot of more details to mention, but I don't know which are relevant to solving this problem. Please ask, if you need more info or have a look at the code (Github).
Many thanks!
The problem is caused by sending the entriesMonths as a property to MonthSelector.vue component,
By that you're violating the purpose of having a store/getters/mutations, you can access the entriesMonths directly from your component, you don't have to pass it as a prop,
So change your MonthSelector.vue as follows:
Remove the property that is passed to the component
In your slideOpts.initialSlide access the entriesMonths directly from the store.
initialSlide: this.$store.getters.entriesMonths.length - 1
*Update: *
Please remove the entriesMonths from the computed properties and put it in data as follows:
data() {
const entriesMonths = this.$store.getters.entriesMonths
return {
entriesMonths,
slideOpts: {
initialSlide: entriesMonths.length - 1, // Start with newest slide/month
speed: 300,
pagination: false,
},
};
},
I have a problem and have been looking for hours to get it resolved, without any success.
In short: in a forEach loop I am adding my const "mijndata" to my array "Signalen". I can see that my array is filled successfully.
However, my data-table component from Vuetify is not showing my data. If I click the sort column or if I do a search, my data suddenly appears. So the problem must be that Vue doesn't detect changes to the "signalen" array.
I have already read multiple other threads on this issue and I have read: https://v2.vuejs.org/v2/guide/list.html#Caveats
But I am so far unable to resolve this issue.
Note: if I do a direct push into my "signalen" array from the results of my Axios calls, I don't have this problem. But unfortunately this is not an option as I am needing to create an object which is then pushed into the array.
Here is my problem in code:
mounted () {
axios.get('/my api url').then(response => {
const signalen = response.data.resources;
signalen.forEach((signaal) => {
const mijndata = {};
axios.get('/my api url').then(response => {
const relatedSensoren = response.data.resources;
relatedSensoren.forEach(relatedSensor => {
axios.get('/my api url').then(response => {
const sensorConfigs = response.data.resources;
sensorConfigs.forEach(sensorConfig => {
axios.get('/my api url').then(response => {
const alarmRegels = response.data.resources;
alarmRegels.forEach(alarmRegel => {
axios.all([axios.get('/my api url')])
.then(axios.spread((...responses) => {
mijndata.signaalid = signaal.ac_id;
mijndata.signaalsettingid = alarmRegel.ar_id;
}));
});
});
});
});
});
});
//A few more gets following the same logic as above...
//Push my const "mijndata" into my array "signalen"
this.signalen.push(mijndata)
});
});
//I can see that all of my objects are in my array "signalen"
window.console.log(this.signalen)
},
My data-table:
<v-data-table
:headers="headers"
:items="signalen"
:search="search"
class="elevation-1"
>
My array in my export:
export default {
data: () => ({
headers: [
{
text: 'Signaalnummer',
align: 'left',
value: 'signaalid',
},
{ text: 'Wanneer gaat dit alarm af?', value: 'signaalsetting' },
{ text: 'Manier van alarmeren', value: 'alarmsetting' },
{ text: 'Geadresseerde', value: 'alarmpersoon' },
{ text: 'Actions', value: 'action', sortable: false },
],
signalen: [],
}),
The headers match the keys from my array. Like I said, the problem is purely in pushing my "mijndata" object into my "signalen" array.
I would really appreciate any help!
Finally found the answer.
I fixed it by using Vue.set on the const "mijndata".
Eg: Vue.set(mijndata, 'signaalid', signaal.ac_id)
I am trying to create a React/Redux app which lists books. Each book then has related books. I know I should structure my redux store in some sort of normalized fashion like so:
{
books: {
'book1id': {
title: 'Book 1',
author: 'Author 1',
relatedBooks: ['book2id', 'book3id']
},
'book2id': {
title: 'Book 2',
author: 'Author 2',
relatedBooks: ['book1id']
}
}
}
and load each book as necessary.
The issue is where to store loading/error data from the API requests? Some ideas I had were to create an object for every book such as
books: {
'book1id': {
isLoading: false,
error: null,
book: {
title: 'Book 1',
...
}
},
...
}
But that seems to detract slightly from the readability/intuitiveness of the state. Is there a better way to structure the state?
I structure my redux store so that it includes an entities object for all of my relational data and I store things specific to a page or a set of routes in separate parts of the state. My state tree might look something like this:
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
isLoading: false
}
}
Now I am keeping track of my relational state separate from the state of my UI components. I would not recommend trying to store a isLoading state in an individual entity since that entity may or may not exist. If you need more granular loading/error state on a per entity basis then rather on a set of entities you have a few options. The first option is keep a set of IDs that are currently loading. This is not a very practical solution because you can't track success or failure with an ID simply being in a set or not.
The next, better solution is to keep a map from ID to a status object that includes if an individual entity is loading, if it was successful or if it failed to load and what the errors were.
const state = {
entities: {
books: {
...
},
authors: {
...
},
...
},
booksPage: {
loading: {
book1: {
status: 'FAILED',
error: 'Network request failed.'
},
book2: {
status: 'SUCCESS',
},
book3: {,
status: 'IN_PROGRESS'
}
}
}
}
In summary, I find separating out your relational state into an entities child object while having page specific sections of state to be working quite well for me.