I use a react module that manages the use of spreadsheets:
react-spreadsheet https://github.com/iddan/react-spreadsheet/
In my project, I need several spreadsheets, and therefore several tables that the user can add, modify, delete on the fly
At the beginning, I wanted to use a big useState variable where the data arrays would be concentrated in the form : stateData = [ [...arrays], [...arrays...] ]
But the module, which uses an OnChange function taking as value the setData, seems to bug when my stateData variable contains different arrays.
<Spreadsheet key={index} className='table' columnLabels={stringLabelCol} rowLabels={stringLabelRow} data={stateData[index]} onChange={setData} />
As I can't manage everything in one UseState with this module,
is it possible to create a series of UseState inside a loop ?
It seems to me that it is a bad practice but I am really blocked
Thank you for reading my text... :/
Seeing your code and your comment, I'm assuming that:
You have one useState named data with ALL your data in it
You have multiple spreadsheets
For each spreadsheet, you want to pass the sheetData and the onChange that updates it
The issue in your code is that you're setting onChange={setData}, meaning any change will override the WHOLE data object, not just the targeted index.
What you want to do is have your onChange function be a function that updates only the right part of your state. So you'd do it like so:
const [data, setData] = useState([])
const updateSheetData = (index, sheetData) => {
// We clone the current state
const tempData = [...data]
// We update the right index
tempData[index] = sheetData
// This is now the new state
setData(tempData)
}
//...
// the onChange function for our component will use our custom function
<Spreadsheet
key={index}
className='table'
columnLabels={stringLabelCol}
rowLabels={stringLabelRow}
data={stateData[index]}
onChange={(newData) => updateSheetData(index, newData)}
/>
Then you'll also have to correctly update the data state whenever you add a new sheet or remove a sheet, to make sure the index are updated. But the idea is the same
Related
I've written a file which contains a Table that should display codes when there are pushed inside the array codes with prop name code (e.x. "848389494")
now every time the function addNewCode is called it should update the _codeData Object but it's not possible because its an outer scouped function. i cant move it inside ScannerResultTable because addNewCode should be able to be exported and used in another Component.
but i dont know how its not possible to useEffect and keep track on "codes" but it wont allow it because it's an outerscouped var.
thanks for your help-
const codes = []
export function addNewCode(value){
codes.push({ code: value });
}
export default function ScannerResultTable() {
const [_codeData, setCodeData] = useState([]);
useEffect(() => {
console.log("Hi im rendering");
setCodeData(codes);
}, []);
const [tableData, setTableData] = useState(_codeData);
Instead of using a globally defined variable codes, you can make use of React context and make the codes a state in some parent components and pass down the utility function to update the codes to the child components that are gonna update it.
More details on how to use react context can be found here
I am in the beginning of learning Vue, and having a hard time understanding how to define props etc. using the composition API.
I have a component (ACE Editor), loaded like this:
<v-ace-editor
v-model:value="currentRecord.text"
/>
The v-ace-editor needs to have the model and value loaded like this: v-model:value.
import {computed, ref} from 'vue';
import collect from "collect.js"
const props = defineProps({
records: {
type: Object,
},
})
//Get all records.
let getRecords = () => {
return collect(props.records)
}
//Filter the records using "collect.js"
const getUnlabeledRecords = () => {
return getRecords()
.where('skipped', false)
.where('processed', false);
}
//Assign all unlabeled records on the first load.
let allRecords = ref(getUnlabeledRecords());
//In order to check if "text" is empty - and if not, set a default value, load the first record into a temp. variable.
let first = allRecords.value.first();
if(first){
first.text ??= "";
}
//Load the current record into a ref.
let current = ref(first);
//Finally, compute it.
let currentRecord = computed(() => current.value)
Looking at this, and coming from a backend background, it feels very bloated.
I have tried the following:
let allRecords = ref(getUnlabeledRecords());
let currentRecord = computed(() => allRecords.value.first())
But doing this leads to me not being able to interact with the currentRecord - nor change the allRecords. This means that if for example currentRecord.text is null from the backend, my ace-editor component fails because it expects a value.
Is there another way to load in these variables?
You actually don't have to called .value of a ref when using it in the template.
So you can actually remove the computed part (last line of your ) and change your template to.
<v-ace-editor
v-model="current.text"
/>
Now, assuming you managed v-model correctly in v-ace-editor (if this is your own component), you should have reactivity kept when modifiying current.text from v-ace-editor.
As a side note, computed properties are read-only. You cannot expect a child component to modify its value by passing it with v-model.
However, you should note that updating records prop from parent component will not update current. For this, maybe you want to add a watcher on records.
Also, personal suggestion, but if you only really care about currentRecord in your component and not all records, maybe you should do the filtering from parent component and only pass currentRecord as a prop. Other personal suggestion, you can declare all your variables in your script with const instead of let. const prevent reassignation, but since you work with refs, you never reassign it, but you change its value property.
I'm using the library React Datasheet Grid, the main component has a property called OnActiveCellChange, to which i'm passing my custom function:
const [selectedCell, setSelectedCell] = useState([])
...
function handleCellSelection({ cell }) {
if(cell) {
const {row, col} = cell;
const newData = data[row][col];
if(!newData.unpickable) {
console.log(selectedCell); // prints empty array
setSelectedCell(selectedCell => [...selectedCell, newData.value]);
}
}
}
...
<DataSheetGrid
className={'overwriteDisabledStyles'}
value={data}
onChange={setData}
columns={columns}
onActiveCellChange={handleCellSelection}
headerRowHeight={false}
gutterColumn={false}
lockRows
/>
The problem is that I wish to make some checks with the state variable "selectedCell", while I managed to update the state from with the functional form, I can't access the the state variable. I managed to use the variable using "useRef".
I want to know if I can tackle the problem in any other way.
EDIT:
Trying to clarify my question:
What I want is to, inside the handleCellSelection function, make some checks before inserting the newData using the setSelectedCell. Like searching the selectedCell array for existing values:
const valueAlreadyExists = selectedCell.some((existingCell) => {
return existingCell === newData.value
});
Yet, when the handleCellSelection function is called, the value of selectedCell is always an empty array. It should be the values that are being inserted in the array. I know there are values there because when I console.log outside the handleCellSelection they appear correctly.
So, I managed to access them inside the handleCellSelection using useRef() but what I am searching for is another option to solve this.
I have a React Apollo app and what I am trying to do is that I have a component that renders some data using charts. For this data, I have some filters that I save in the local state of the component (Using hooks)
const [filters, setFilters] = useState(defaultFilters);
Now what I want is that whenever the component mounts, fetch the data using the default filters. But I also want to re-fetch data when the user updates the filters AND CLICKS ON SUBMIT and I'd fetch the results using new filters.
Since I also want to fetch the results on filter update, I am using useLazyQuery hook provided by apollo
const [getData, {data}] = useLazyQuery(GET_DATA_QUERY, { variables: {filters} });
useEffect(getData, []); // this useEffect runs only when the component mounts and never again
But, what happens is whenever my state, filters, updates the getData function is automatically run! ALWAYS! (BEHIND THE SCENE)
How do I handle such cases, where I want to fetch results on mounting and re-rendering.
I have tried using useQuery and refetch provided by it but I get the same problem there, whenever I update the state, the component rerenders and the useQuery hooks is run and makes the call. (That's how I believe it runs)
How do I fix my current code. Calling the getData function inside the useEffect function makes it run on every re-render.
I think I the problem defined in this stackoverflow-question is somewhat similar to mine.
Part of the problem is that you really have two different states that you're trying to utilize a single hook for. You have state that represents your inputs' values in the UI, and then you have state that represents the filters you want to actually apply to your charts. These are two separate bits of state.
The simplest solution is to just do something like this:
const [inputFilters, setInputFilters] = useState(defaultFilters)
const [appliedFilters, setAppliedFilters] = useState(inputFilters)
const { data } = useQuery(GET_DATA_QUERY, { variables: { filters: appliedFilters } })
const handleSubmit = () => setAppliedFilters(inputFilters)
const handleSomeInputChange = event => setInputFilters(...)
This way, you use inputFilters/setInputFilters only to manage your inputs' state. When the user clicks your submit button, the appliedFilters are set to whatever the inputFilters are at the time, and your query will update to reflect the new variables.
I want to create react table component which values are derived from single array object. Is it possible to control the component from view side? My goal is that every user using this component in their web browsers share the same data via singleton view object.
Program modeling is like below.
Database - there are single database in server which contain extinct and independent values.
DataView - there are singleton View class which reflects Database's table and additional dependent data like (sum, average)
Table - I'll build react component which looks like table. And it will show View's data with supporting sorting, filtering, editing and deleting row(s) feature (and more). Also it dose not have actual data, only have reference of data from View(Via shallow copy -- This is my question, is it possible?)
My intentions are,
- When user changes value from table, it is queried to DB by View, and if succeed, View will refer updated data and change it's value to new value and notify to Table to redraw it's contents. -- I mean redraw, not updating value and redraw.
- When values in View are changed with DB interaction by user request, there are no need to update component's value cause the components actually dose not have values, only have references to values (Like C's pointer). So only View should do is just say to Component to redraw it's contents.
I heard that React's component prop should be immutable. (Otherwise, state is mutable) My goal is storing references to component's real value to it's props so that there are no additional operation for reflecting View's data into Table.
It is concept problems, and I wonder if it is possible. Since javascript dose not support pointer officially(Am I right?), I'm not sure if it is possible.
View class is like below,
const db_pool = require('instantiated-singleton-db-pool-interface')
class DataView {
constructor() {
this.sessions = ['user1', 'user2'] // Managing current user who see the table
this.data = [ // This is View's data
{id:1, name:'James', phone:'12345678', bank:2000, cash:300, total:2300,..},
{id:2, name:'Michael', phone:'56785678', bank:2500, cash:250, total:2300,..},
{id:3, name:'Tyson', phone:'23455432', bank:2000, cash:50, total:2300,..}
] // Note that 'total' is not in db, it is calculated --`dependent data`
}
notifySessionToUpdate(ids) {
// ids : list of data id need to be updated
this.sessions.forEach((session) => {
session.onNotifiedUpdateRow(ids) // Call each sessions's
})
}
requestUpdateRow(row, changed_value) {
// I didn't write async, exception related code in this function for simple to see.
update_result = db_pool.update('UPDATE myTable set bank=2500 where id=1')
if (update_result === 'fail') return; // Do Nothing
select_result = db_pool.select('SELECT * from myTable where id=1') // Retrieve updated single data which object scheme is identical with this.data's data
for (k in Object.keys(select_result)) {.ASSIGN_TO_row_IF_VALUE_ARE_DIFFERENT.} // I'm not sure if it is possible in shallow copy way either.
calc.reCalculateRow(row) // Return nothing just recalculate dependant value in this.data which is updated right above.
// Notify to session
this.notifySessionToUpdate([1]) // Each component will update table if user are singing id=1's data if not seeing, it will not. [1] means id:1 data.
return // Success
}
... // other View features
}
Regarding session part, I'm checking how to implement sessionizing(?) the each user and it's component who is communicating with server. So I cannot provide further codes about that. Sorry. I'm considering implementing another shallow copied UserView between React Component Table and DataView(And also I think it helps to do something with user contents infos like sorting preference and etc...)
Regarding DB code, it is class which nest it's pool and query interface.
My problem is that I'm not familiar with javascript. So I'm not sure shallow copy is actually implementable in all cases which I confront with.
I need to think about,
1. Dose javascript fully support shallowcopy in consistent way? I mean like pointer, guarantee check value is reference or not.
2. Dose react's component can be used like this way? Whether using props or state Can this be fullfilled?
Actually, I strongly it is not possible to do that. But I want to check your opinions. Seems it is so C language-like way of thinking.
Redraw mean re-render. You can expose setState() or dispatch() functions from Table component and call them on View level using refs:
function View() {
const ref = useRef();
const onDbResponse = data => ref.current.update(data);
return (
<Table ref={ ref } />
);
}
const Table = React.forwardRef((props, ref) => {
const [ data, setData ] = useState([]);
useImperativeHandler(ref, {
update: setData
});
...
});
Anyway i don't think it's a good practice to update like that. Why can't you just put your data in some global context and use there?
const Context = React.createContext({ value: null, query: () => {} });
const Provider = ({ children }) => {
const [ value, setValue ] = useState();
const query = useCallback(async (request) => {
setValue(await DB.request(request));
}, [ DB ]);
const context = { value, query };
return <Context.Provider value={ context }>{ children }</Context.Provider>;
}
const useDB = () => useContext(Context);
const View = () => {
const { request } = useDB();
request(...);
}
const Table = () => {
const { value } = useDB();
...
}