How to access ag-Grid API in React function component (useState hook)? - function

What is the best way of accessing ag-Grid API inside of React function component?
I have to use some of the methods from API (getSelectedNodes, setColumnDefs etc.) so I save a reference to the API (using useState hook) in onGridReady event handler:
onGridReady={params => {
setGridApi(params.api);
}}
and then I can call the API like this: gridApi.getSelectedNodes()
I haven't noticed any problems with this approach, but I'm wondering if there's more idiomatic way?
Stack:
ag-grid-community & ag-grid-react 22.1.1
react 16.12.0

We find the most idiomatic way to use a ref. As the api is not a state of our component. It is actually possible to simply do:
<AgGridReact ref={grid}/>
and then use it with
grid.current.api
Here an example:
import React, { useRef } from 'react'
import { AgGridReact } from 'ag-grid-react'
import { AgGridReact as AgGridReactType } from 'ag-grid-react/lib/agGridReact'
const ShopList = () => {
const grid = useRef<AgGridReactType>(null)
...
return (
<AgGridReact ref={grid} columnDefs={columnDefs} rowData={shops} />
)
}
The good thing here is, that you will have access to the gridApi but als to to the columnApi. Simply like this:
// rendering menu to show/hide columns:
{columnDefs.map(columnDef =>
<>
<input
type='checkbox'
checked={
grid.current
? grid.current.columnApi.getColumn(columnDef.field).isVisible()
: !(columnDef as { hide: boolean }).hide
}
onChange={() => {
if (grid.current?.api) {
const col = grid.current.columnApi.getColumn(columnDef.field)
grid.current.columnApi.setColumnVisible(columnDef.field, !col.isVisible())
grid.current.api.sizeColumnsToFit()
setForceUpdate(x => ++x)
}
}}
/>
<span>{columnDef.headerName}</span>
</>
)}

Well I am doing it in my project. You can use useRef hook to store gridApi.
const gridApi = useRef();
const onGridReady = params => {
gridApi.current = params.api; // <== this is how you save it
const datasource = getServerDataSource(
gridApi.current,
{
size: AppConstants.PAGE_SIZE,
url: baseUrl,
defaultFilter: props.defaultFilter
}
);
gridApi.current.setServerSideDatasource(datasource); // <== this is how you use it
};

I'm running into the same issue but here is a workaround that at least can get you the selected rows. Essentially what I'm doing is sending the api from the agGrid callbacks to another function. Specifically I use OnSelectionChanged callback to grab the current row node. Example below:
const onSelectionChanged = params => {
setDetails(params.api.getSelectedRows());
};
return (<AgGridReact
columnDefs={agData.columnDefs}
rowSelection={'single'}
enableCellTextSelection={true}
defaultColDef={{
resizable: true,
}}
rowHeight={50}
rowData={agData.rowData}
onCellFocused={function(params) {
if (params.rowIndex != null) {
let nNode = params.api.getDisplayedRowAtIndex(params.rowIndex);
nNode.setSelected(true, true);
}
}}
onSelectionChanged={function(params) {
onSelectionChanged(params);
params.api.sizeColumnsToFit();
}}
onGridReady={function(params) {
let gridApi = params.api;
gridApi.sizeColumnsToFit();
}}
deltaRowDataMode={true}
getRowNodeId={function(data) {
return data.id;
}}
/>);

Related

Change the input initial value of controlled component in React

I have this input component
const FOO = props => {
const [inputValue, setInputValue] = useState(
props.editState ? props.initialValue : ""
);
const setSearchQuery = (value) => {
setInputValue(value);
props.onSearch(value);
};
return (
<input
placeholder="Select ..."
onChange={(e) => {
setSearchQuery(e.target.value);
}}
value={inputValue}
/>
)}
I'm using it like this
const BAR = props => {
const [fetchedData, setfetchedData] = useState({
value : "" // to get rid of can't change controlled component from undefined error
});
const params = useParams();
//request here to get fetchedData
return(
<FOO
onSearch={(value) => {
searchSomethingHandler(value);
}}
initialValue={
params.ID
? fetchedData.value
: ""
}
editState={params.ID ? true : false}
/>
)}
I need to set the initial value of the fetched data into the input so the user could see the old value and edit it, if I pass the data (fetchedData) as props it works perfectly,
but if I get the data through API it wont set the value cause it's empty at the first render,
how can I solve this please?
You'll probably want to make use of the useEffect hook to run code when a value updates.
In FOO:
const FOO = props => {
// ...
useEffect(() => {
// This hook runs when props.initialValue changes
setInputValue(props.initialValue);
}, [props.initialValue]);
// ...
};
You can leave BAR the same as before, I think. Though, I would put the request to the API inside a useEffect hook with an empty dependency array so you're not querying it on every render.

How execute this react-query mutation?

I have problem understanding this piece of code. How i can pass the id and data ?
function usePutCompany(id) {
const [putCompany] = useMutation<any, any, any>(
(data) => ApiCall.Company.put(id, data),
{
onSuccess() {
queryCache.invalidateQueries('company')
queryCache.refetchQueries('company')
},
throwOnError: true,
},
)
return putCompany
}
id is a parameter to the custom hook, and data is a parameter to the function returned by the hook. You would use this custom hook like this:
function MyComponent() {
const putCompany = usePutCompany(1)
return <Button onClick={() => putCompany(data) />
}

Mapping JSON data with React Hooks

I'm working on a small project and I am trying to map data from a JSON file into my project.
In components with nested data, I keep getting an let data = props.data["runways"];.
data.json:
{
"runways":[
{
"slot":"Area 1",
"planes":[
{
"name":"PanAm",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
},
{
"name":"PanAm 222 ",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
}
]
}
]
}
App.js,
I pass the JSON data as props:
import planeData from './plane_info.json'
const Container = () => {
const [planeDataState, setPlaneDataState] = useState({})
const planeData = () => setPlaneDataState(planeData[0].runways)
return (
<>
<MyPlane planeInfo={planeDataState}/>
<button onClick={planeData} type="button">Get Data</button>
</>
)
}
and finally, I want to bring my data into my component:
MyPlane.jsx
const MyPlane = (props) => {
let data = props.data["runways"];
if(data)
console.log(data, 'aaa')
return (
<>
{
data ? (
<div>
<span>{props.planeInfo.name}</span>
<span>RAIL TYPE: {props.planeInfo.type}</span>
</div>
) : <h6>Empty</h6>
}
</>
);
}
According to the error message, the problem occurs at this line of code: let data = props.data["runways"]; However, I believe that I am passing the data for runways from the JSON file.
I've never worked with React Hooks to pass data, so I'm confused about why this error is occurring.
In order to map effectively over the JSON data it's necessary to understand how that data structure is composed.
If you're unsure, using JSON.stringify() is a great way to get the "bigger picture" and then decide what exactly is it that you want to display or pass down as props to other components.
It appears you wish to get the plane data (which is currently an array of 2 planes). If so, you could first get that array, set the state, then map over it to display relevant info. Perhaps like this:
const data = {
"runways":[
{
"slot":"Area 1",
"planes":[
{
"name":"PanAm",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
},
{
"name":"PanAm 222 ",
"number":"12345",
"start":{
"time":1585129140
},
"end":{
"time":1585130100
}
}
]
}
]
}
function App() {
const [ planeData, setPlaneData ] = React.useState(null)
React.useEffect(() => {
setPlaneData(data.runways[0].planes)
}, [])
return (
<div className="App">
{/* {JSON.stringify(planeData)} */}
{planeData && planeData.map(p => (
<p key={p.name}>
{p.name} | {p.number} | {p.start.time} | {p.end.time}
</p>
))}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Here const planeData = () => setPlaneDataState(planeData[0].runways)
In this line, planeData[0].runways will be undefined according to the json file which you have shared.
Instead try setting and passing entire json object, ie,
const planeData = () => setPlaneDataState(planeData)
Try this, And then inside MyPlane.jsx component, let data = props.data["runways"]; this won't be undefined. So , the error won't come.
At the beginning there is no data in props.data['runways'] (also you can use props.data.runways, I guess you come from another language like Python as of this syntax that you are using), because you sent the request at first, it takes time for request to be satisfied, so you need to check in your <MyPlane /> component to see if there is a runways key in data and then proceed to render the component, something like below:
const MyPlane = (props) => {
const data = props.data
return (
<>
{
data.runways
? <>
...your render able items that you wrote before
</>
: <p>There is no data yet!</p>
}
</>
)
}
Also please note that you might return something from component. At your case your render is inside the if(data){...} statement! what if the condition was not satisfied? which is your current error case !
NOTE: please check that you are passing your planeDataState as planeInfo prop to the child component, so you might have something like:
const data = props.planInfo
to be able to use the data variable that you've defined before the render part.

Trying to use local-storage in react app, however not working in between pages

I'm trying to save an array to local-storage in my react app, so that if the user goes to another page in the app, or closes the app and reopens it, the value stays the same.
In my index.js (simplified code):
import ls from 'local-storage';
function HomeIndex() {
const [testString, setTestString] = useState(ls('localStorageText') || '');
if(condition){
const array = [1,2,3];
const saveArray = {key: array};
localStorage.setItem('key1', JSON.stringify(saveArray));
const restoreValue = localStorage.getItem('key1');
setTestString(JSON.parse(restoreValue).key);
}
return (
<div className="col-12">
{testString}
</div>
);
}
When I press the button, and the condition is met, the testString value displays 123 as it should. And it holds the value. However it does not work when I try and add my own array.
const array = reversedHistoryText;
const saveArray = {key: array};
localStorage.setItem('key1', JSON.stringify(saveArray));
const restoreValue = localStorage.getItem('key1');
setTestString(JSON.parse(restoreValue).key);
It doesn't display anything the first time the button is clicked, then gives error on the 2nd time:
Error: Minified React error #31;
When I do this test:
setTestString(JSON.stringify(reversedHistoryText));
The result is []
You need to set your testString to the localStorage value.
import ls from "local-storage";
import React, { useEffect, useState } from "react";
function MyComponent() {
const [testArray, setTestArray] = useState([]);
useEffect(() => {
setTestArray(ls("testArray") || []);
}, []);
function handleClick(e) {
ls("testArray", [
{ id: 1, name: "this" },
{ id: 2, name: "thing" },
{ id: 3, name: "is" },
{ id: 4, name: "cool" }
]);
setTestArray(ls("testArray"));
}
return (
<div className="col-12">
<ul>
{testArray.map(obj => (
<p key={obj.id}>{obj.name}</p>
))}
</ul>
<button onClick={handleClick}>Set The State</button>
</div>
);
}
export default MyComponent;
You don't need to use third party for localStorage.
Just use localStorage without importing anything.
To save,
localStorage.setItem('key', 'value');
To get value from localStorage,
localStorage.getItem('key') // value
To remove value,
localStorage.removeItem('key')
Use
if(condition) ls('localStorageText', "TEST");
setTestString(ls('localStorageText')|| ' '); }
Instead
if(condition){ ls('localStorageText', "TEST");
setTestString(ls('localStorageText')); }
Because when you go back to the index page a new instance of this component is rendered and i think the condition in the if statement is false, so the code don't change the setstate value...
To set use
localStorage.setItem('itemName', JSON.stringify(arrayName));
To get use
whatEver = jQuery.parseJSON(localStorage.getItem('itemName'));
Local storage stores strings

Too tidious hooks when querying in REST. Any ideas?

I've just started using feathers to build REST server. I need your help for querying tips. Document says
When used via REST URLs all query values are strings. Depending on the service the values in params.query might have to be converted to the right type in a before hook. (https://docs.feathersjs.com/api/databases/querying.html)
, which puzzles me. find({query: {value: 1} }) does mean value === "1" not value === 1 ? Here is example client side code which puzzles me:
const feathers = require('#feathersjs/feathers')
const fetch = require('node-fetch')
const restCli = require('#feathersjs/rest-client')
const rest = restCli('http://localhost:8888')
const app = feathers().configure(rest.fetch(fetch))
async function main () {
const Items = app.service('myitems')
await Items.create( {name:'one', value:1} )
//works fine. returns [ { name: 'one', value: 1, id: 0 } ]
console.log(await Items.find({query:{ name:"one" }}))
//wow! no data returned. []
console.log(await Items.find({query:{ value:1 }})) // []
}
main()
Server side code is here:
const express = require('#feathersjs/express')
const feathers = require('#feathersjs/feathers')
const memory = require('feathers-memory')
const app = express(feathers())
.configure(express.rest())
.use(express.json())
.use(express.errorHandler())
.use('myitems', memory())
app.listen(8888)
.on('listening',()=>console.log('listen on 8888'))
I've made hooks, which works all fine but it is too tidious and I think I missed something. Any ideas?
Hook code:
app.service('myitems').hooks({
before: { find: async (context) => {
const value = context.params.query.value
if (value) context.params.query.value = parseInt(value)
return context
}
}
})
This behaviour depends on the database and ORM you are using. Some that have a schema (like feathers-mongoose, feathers-sequelize and feathers-knex), will convert values like that automatically.
Feathers itself does not know about your data format and most adapters (like the feathers-memory you are using here) do a strict comparison so they will have to be converted. The usual way to deal with this is to create some reusable hooks (instead of one for each field) like this:
const queryToNumber = (...fields) => {
return context => {
const { params: { query = {} } } = context;
fields.forEach(field => {
const value = query[field];
if(value) {
query[field] = parseInt(value, 10)
}
});
}
}
app.service('myitems').hooks({
before: {
find: [
queryToNumber('age', 'value')
]
}
});
Or using something like JSON schema e.g. through the validateSchema common hook.