This is my output that I get from this GET url: https://localhost/get-all
but I can't save this value in the useState: const [dataCat, setDataCat] = useState([])
When I display it in the console, it is displayed correctly, but it returns an empty array in the state
{
"categories": [
{
"id": 1,
"name": "test1",
"slug": "intelligence-and-memory-tests",
"description": null,
},
{
"id": 2,
"name": "test2",
"slug": "occupational-and-organizational-tests",
"description": null,
},
{
"id": 3,
"name": "test3",
"slug": "love-and-marriage-tests",
},
]
}
this is my useEffect:
useEffect(() => {
const fetchData = async () =>{
try {
const {data} = await axios.get('https://localhost/get-all');
console.log(data);
setDataCat(data)
} catch (error) {
console.error(error.message);
}
}
fetchData();
}, []);
You can display it like this, and it will store the data in your useState(). I've created that formattedData to recreate your object
import { useEffect, useState } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [dataCat, setDataCat] = useState([]);
const [newDataCat, setNewDataCat] = useState([]);
// console.log("dataCat", dataCat);
// console.log("newDataCat", newDataCat);
const formattedData = (infoData) => {
let newDataCat = [];
infoData.forEach((item) =>
newDataCat.push({
id: item.id,
name: item.name,
slug: item.slug,
description: item.description
})
);
return newDataCat;
};
useEffect(() => {
const fetchData = async () => {
try {
const { data } = await axios.get(
"https://localhost/get-all"
);
setDataCat(data.categories);
} catch (error) {
console.error(error.message);
}
};
fetchData();
}, []);
useEffect(() => {
const cat = formattedData(dataCat);
setNewDataCat(cat);
}, [dataCat]);
return (
<>
{newDataCat &&
newDataCat.map((item) => {
return (
<>
<h2>{item.id}</h2>
<h2>{item.name}</h2>
<h2>{item.slug}</h2>
</>
);
})}
</>
);
}
Related
I was copying code from this solution: Is there a way to use leaflet.heat in react? and I'm getting an error
TypeError: map.addLayer is not a function
in the currentPosition function
export const currentPosition = atom({
key: "currentPosition",
default: [-2.600, -11.01],
});
in the getLocationCity function
export const getLocationCity = async (params) => {
try {
const body = DataCustom(params);
const result = await instanceArcgis.post(
"/rest/HomeLocation/11/query",
body,
);
return await result?.data;
}catch (error) {
console.log(error);
}
I'm sure it's a var or placement order issue, but I tried various options and it still doesn't work. And it outputs results from the API but no Heatmap color comes out just 'Marker'
Full JS code:
import React, { useEffect, useState, useRef } from "react";
import { GeoJSON } from "react-leaflet";
import { useRecoilValue, useSetRecoilState } from "recoil";
import HeatmapOverlay from "leaflet-heatmap";
import { arcgisToken } from "../recoil";
import { mapBounds, loadingMap } from "../state";
import { getLocationCity } from "../data/arcgis";
import "leaflet.heat";
export default function HSales() {
const [data, setData] = useState();
const setIsLoading = useSetRecoilState(loadingMap);
const bounds = useRecoilValue(mapBounds);
const tokenArcgis = useRecoilValue(arcgisToken);
const geoJsonLayer = useRef(null);
useEffect(() => {
const fetchDataSpeedtestKel = () => {
const body = {
returnGeometry: true,
rollbackOnFailure: true,
geometry: JSON.stringify({
xmin: bounds.west,
ymin: bounds.south,
xmax: bounds.east,
ymax: bounds.north,
spatialReference: { wkid: 4326 },
}),
geometryType: "esriGeometryEnvelope",
token: tokenArcgis,
};
setIsLoading(true);
getLocationCity(body)
.then((response) => {
const array = [];
response.features.forEach((element) => {
array.push({
type: "Feature",
properties: element["attributes"],
geometry: {
type: "Point",
coordinates: [
element["geometry"]["x"],
element["geometry"]["y"],
],
},
});
});
const FeatureCollection = {
type: "FeatureCollection",
features: array,
};
if (geoJsonLayer.current) {
geoJsonLayer.current.clearLayers().addData(FeatureCollection);
}
const points = response.features
? response.features.map((element) => {
return [
element["geometry"]["x"],
element["geometry"]["y"]
];
})
: [];
setData(FeatureCollection);
L.heatLayer(points).addTo(position);
setPosition(data);
})
.catch((err) => console.log(err))
.finally(() => setIsLoading(false));
};
fetchDataSpeedtestKel();
}, [bounds, setIsLoading, tokenArcgis]);
if (data) {
return (
<>
<GeoJSON
ref={geoJsonLayer}
data={data}
/>
</>
);
}
}
Thank you so much! Jim
setData(FeatureCollection);
L.heatLayer(points).addTo(position);
setPosition(data);
"setData" this update function doesn’t update the value right away.
Rather, it enqueues the update operation. Then, after re-rendering the component, the argument of useState will be ignored and this function will return the most recent value inside data.
May be you can try something like setPosition(FeatureCollection);
This is the total code that I currently have.
import { React, useEffect, useState } from 'react';
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-balham.css";
const FetchStocks = () => {
const API_KEY = "apiKey1";
const API_KEY2 = "apiKey2";
const API_KEY3 = "apiKey3";
const [data, setData] = useState({ StockSymbols: null, StockName: null, StockIndustry: null })
const [MSFT, setMSFT] = useState({ MSFTSymbols: null, MSFTName: null, MSFTIndustry: null })
const [AA, setAA] = useState({ AASymbols: null, AAName: null, AAIndustry: null })
const [BABA, setBABA] = useState({ BABASymbols: null, BABAName: null, BABAIndustry: null })
const [SAIC, setSAIC] = useState({ SAICSymbols: null, SAICName: null, SAICIndustry: null })
const [search, setSearch] = useState < string > ('');
useEffect(() => {
fetch(`https://www.alphavantage.co/query?function=OVERVIEW&symbol=IBM&apikey=${API_KEY}`)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
setData({
StockSymbols: data['Symbol'],
StockName: data['Name'],
StockIndustry: data['Industry']
})
})
fetch(`https://www.alphavantage.co/query?function=OVERVIEW&symbol=MSFT&apikey=${API_KEY2}`)
.then(
function (response) {
return response.json();
}
)
.then(
function (MSFT) {
setMSFT({
MSFTSymbols: MSFT['Symbol'],
MSFTName: MSFT['Name'],
MSFTIndustry: MSFT['Industry']
})
})
fetch(`https://www.alphavantage.co/query?function=OVERVIEW&symbol=AA&apikey=${API_KEY3}`)
.then(
function (response) {
return response.json();
}
)
.then(
function (AA) {
setAA({
AASymbols: AA['Symbol'],
AAName: AA['Name'],
AAIndustry: AA['Industry']
})
})
fetch(`https://www.alphavantage.co/query?function=OVERVIEW&symbol=BABA&apikey=${API_KEY}`)
.then(
function (response) {
return response.json();
}
)
.then(
function (BABA) {
setBABA({
BABASymbols: BABA['Symbol'],
BABAName: BABA['Name'],
BABAIndustry: BABA['Industry']
})
})
fetch(`https://www.alphavantage.co/query?function=OVERVIEW&symbol=SAIC&apikey=${API_KEY2}`)
.then(
function (response) {
return response.json();
}
)
.then(
function (SAIC) {
setSAIC({
SAICSymbols: SAIC['Symbol'],
SAICName: SAIC['Name'],
SAICIndustry: SAIC['Industry']
})
})
}, [])
const table = {
columns: [
{ headerName: "Symbol", field: "symbol" },
{ headerName: "Name", field: "name" },
{ headerName: "Industry", field: "industry" }
],
rowData: [
{ symbol: `${data.StockSymbols}`, name: `${data.StockName}`, industry: `${data.StockIndustry}` },
{ symbol: `${MSFT.MSFTSymbols}`, name: `${MSFT.MSFTName}`, industry: `${MSFT.MSFTIndustry}` },
{ symbol: `${AA.AASymbols}`, name: `${AA.AAName}`, industry: `${AA.AAIndustry}` },
{ symbol: `${BABA.BABASymbols}`, name: `${BABA.BABAName}`, industry: `${BABA.BABAIndustry}` },
{ symbol: `${SAIC.SAICSymbols}`, name: `${SAIC.SAICName}`, industry: `${SAIC.SAICIndustry}` }
],
}
let containerStyle = {
height: 500,
width: 700
}
return (
<div>
<div>
<input type="search" placeholder="Search Stock" />
</div>
<div
className="ag-theme-balham"
style={containerStyle}
>
<AgGridReact
columnDefs={table.columns}
rowData={table.rowData}
pagination={true}
/>
</div>
</div>
)
};
export default FetchStocks;
I'm trying to make search bar for the symbols column in the table.
This is the table
However, I'm concerned because every element in the table is fetched and saved in differenct const (eg. data, MSFT, AA).
How would I be able to create a search bar that searches by the stock symbol in the table?
One of the easiest way I can think of is to use filter method on 'rowData' property of 'table'.
rowData: [
{
symbol: `${data.StockSymbols}`,
name: `${data.StockName}`,
industry: `${data.StockIndustry}`
}
].filter((data) => {
return data.name.includes(search);
})
Add setSearch to onChange eventHandler of input Element.
In here, I have shown to use name of the stock, you can also use industry and filter based on that.
Attached, codesandbox link
I am using react hooks forms, and I am trying to set the default values of a form that is outputted by mapping over an array and outputting the inputs in the form. I have reduced the array to an object like this {name0:"fijs",name1:"3838"...} and if I manually pass that in the default values it maps to my inputs and populates them. However if I enter them from the variable that is doing the reduce function it doesn't populate it. I think it is because on first render it is undefined. I have tried using a useEffect, but that didn't work so I am stuck.
This is the part of the code I am working on
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
const { register, handleSubmit, errors } = useForm({
defaultValues: test,
});
console.log(test);
and this is the whole thing
import { useQuery, gql, useMutation } from "#apollo/client";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import { useForm } from "react-hook-form";
const INPUT_VALUES = gql`
query GetInputValues {
allFormInputVals {
data {
name
_id
type
}
}
}
`;
const ADD_INPUT_VALUES = gql`
mutation AddInputValues(
$name: String!
$type: String!
$index: Int!
$ID: ID!
) {
createFormInputVal(
data: {
name: $name
type: $type
index: $index
formRoot: { connect: $ID }
}
) {
name
}
}
`;
const Home = () => {
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
};
const [formState, setFormState] = useState([blankFormInput]);
const [formStateVals, setFormStateVals] = useState(undefined);
const { loading, error, data } = useQuery(INPUT_VALUES);
const [createFormInputVal, { data: createInputData }] = useMutation(
ADD_INPUT_VALUES
);
useEffect(() => {
setFormState(data?.allFormInputVals?.data);
}, [data]);
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
const { register, handleSubmit, errors } = useForm({
defaultValues: test,
});
console.log(test);
const onSubmit = (data) => console.log(data);
console.log(errors);
const addInput = async () => {
const blanktext = {
__typename: "FormInputVal",
name: "Product Image",
_id: uuidv4(),
type: "text",
};
setFormState([...formState, { ...blanktext }]);
console.log(formState);
const res = await createFormInputVal({
variables: {
name: "test",
type: "text",
index: 0,
ID: "291541554941657608",
},
}).catch(console.error);
console.log(res);
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input type="button" value="Add Form Input" onClick={addInput} />
{formState?.map((val, idx) => {
const nameId = `name${idx}`;
const typeId = `type-${idx}`;
return (
<div key={val._id}>
{val.type === "text" && (
<>
<label htmlFor={nameId}>{`${val.name} #${idx + 1}`}</label>
<input
type="text"
name={nameId}
id={nameId}
className={val.type}
ref={register()}
/>
{/* <label htmlFor={typeId}>{`Type #${idx + 1}`}</label>
<select name={typeId} id={typeId} className={val.type}>
{data.allFormInputVals.data.map((item) => {
return (
<option key={item._id} value={item.type}>
{item.type}
</option>
);
})}
</select> */}
</>
)}
</div>
);
})}
<button type="submit">Save Form</button>
</form>
</>
);
};
export default Home;
UPDATE: I have tried useEffect with a reset from the api, I thought this was the solution, but still no dice.
const { register, handleSubmit, errors, reset } = useForm();
useEffect(() => {
const result = test; // result: { firstName: 'test', lastName: 'test2' }
reset(result); // asynchronously reset your form values
}, [reset]);
UPDATE: I abstracted the Form to it;s own component, but it still does not work.
Form.js
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useQuery, gql, useMutation } from "#apollo/client";
import { v4 as uuidv4 } from "uuid";
const ADD_INPUT_VALUES = gql`
mutation AddInputValues(
$name: String!
$type: String!
$index: Int!
$ID: ID!
) {
createFormInputVal(
data: {
name: $name
type: $type
index: $index
formRoot: { connect: $ID }
}
) {
name
}
}
`;
export default function Form({ formState, setFormState }) {
const test = formState?.reduce((obj, item, idx) => {
return { ...obj, [`${item.name}${idx}`]: "fdsjfs" };
}, {});
console.log(test);
const { register, handleSubmit, errors } = useForm({ defaultValues: test });
const [formStateVals, setFormStateVals] = useState(undefined);
// console.log(test);
const onSubmit = (data) => console.log(data);
console.log(errors);
const addInput = async () => {
const blanktext = {
__typename: "FormInputVal",
name: "Product Image",
_id: uuidv4(),
type: "text",
};
setFormState([...formState, { ...blanktext }]);
console.log(formState);
const res = await createFormInputVal({
variables: {
name: "test",
type: "text",
index: 0,
ID: "291541554941657608",
},
}).catch(console.error);
console.log(res);
};
const [createFormInputVal, { data: createInputData }] = useMutation(
ADD_INPUT_VALUES
);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input type="button" value="Add Form Input" onClick={addInput} />
{formState?.map((val, idx) => {
const nameId = `name${idx}`;
const typeId = `type-${idx}`;
return (
<div key={val._id}>
{val.type === "text" && (
<>
<label htmlFor={nameId}>{`${val.name} #${idx + 1}`}</label>
<input
type="text"
name={nameId}
id={nameId}
className={val.type}
ref={register()}
/>
{/* <label htmlFor={typeId}>{`Type #${idx + 1}`}</label>
<select name={typeId} id={typeId} className={val.type}>
{data.allFormInputVals.data.map((item) => {
return (
<option key={item._id} value={item.type}>
{item.type}
</option>
);
})}
</select> */}
</>
)}
</div>
);
})}
<button type="submit">Save Form</button>
</form>
);
}
index.js
import { useQuery, gql, useMutation } from "#apollo/client";
import { useEffect, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import Form from "../components/Form";
const INPUT_VALUES = gql`
query GetInputValues {
allFormInputVals {
data {
name
_id
type
}
}
}
`;
const Home = () => {
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
};
const [formState, setFormState] = useState([blankFormInput]);
const { loading, error, data } = useQuery(INPUT_VALUES);
useEffect(() => {
const formData = data?.allFormInputVals?.data;
setFormState(formData);
}, [data]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<>
<Form formState={formState} setFormState={setFormState} />
</>
);
};
export default Home;
You could extract the form to its own component and only render it when the data is fetched. This way, when you use useForm in the child component, the default values will be set properly.
const Home = () => {
const { loading, error, data } = useQuery(INPUT_VALUES)
const blankFormInput = {
__typename: "FormInputVal",
name: "test",
_id: uuidv4(),
type: "text",
}
const [formState, setFormState] = useState([blankFormInput])
// other code
if (loading) {
return <p>Loading...</p>
}
return <MyForm defaultValues={formState} />
}
If you don't want to change the structure, you could set the input values using setValue when the data is ready.
useEffect(() => {
const formData = data?.allFormInputVals?.data
setFormState(formData)
formData?.forEach((item, idx) => {
setValue(`${item.name}${idx}`, 'whatever')
})
}, [data])
I'm trying to build a treeview component in react where data for the tree is fetched based on the nodes expanded by the user.
Problem
I want to replace the code inside handleChange with data from my server, so that I append the data i fetch to the tree state. How can I achieve this with react?
The data i get can look like this:
{
"children": [
{
"id": "2212",
"parentId": "3321",
"name": "R&D",
"address": "homestreet"
},
{
"id": "4212",
"parentId": "3321",
"name": "Testing",
"address": "homestreet"
}
]
}
My Code
import React, { useState } from "react";
import { makeStyles } from "#material-ui/core/styles";
import TreeView from "#material-ui/lab/TreeView";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import TreeItem from "#material-ui/lab/TreeItem";
const useStyles = makeStyles({
root: {
height: 216,
flexGrow: 1,
maxWidth: 400
}
});
export default function FileSystemNavigator() {
const classes = useStyles();
const initialData = {
root: [
{
id: "1",
label: "Applications"
}
],
};
const [tree, setTree] = useState(initialData);
const handleChange = (event, nodeId) => {
setTimeout(() => {
const newTree = {
...tree,
[nodeId]: [
{
id: "2",
label: "Calendar"
},
{
id: "3",
label: "Settings"
},
{
id: "4",
label: "Music"
}
]
};
setTree(newTree);
}, 1000); // simulate xhr
};
const renderTree = children => {
return children.map(child => {
const childrenNodes =
tree[child.id] && tree[child.id].length > 0
? renderTree(tree[child.id])
: [<div />];
return (
<TreeItem key={child.id} nodeId={child.id} label={child.label}>
{childrenNodes}
</TreeItem>
);
});
};
return (
<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
onNodeToggle={handleChange}
>
{renderTree(tree.root)}
</TreeView>
);
}
If I am understanding correctly, you want to replace your "fake" setTimeout implementation of an API call with a real call using fetch.
In this case, it's as simple as calling fetch inside of the handleChange handler and updating your state with new items that you get back as a result.
function FileSystemNavigator() {
const initialData = {...}
const [tree, setTree] = React.useState(initialData)
const handleChange = (event, nodeId) => {
const handleResult = (data) => {
const items = data.children.map(item => {
return { id: item.id, label: item.name }
})
setTree({
root: [...tree.root, ...items]
})
}
const handleError = (error) => {
// handle errors appropriately
console.error(error.message)
}
fetch("https://api.myjson.com/bins/1aqhsc")
.then(res => res.json())
.then(handleResult)
.catch(handleError)
}
// ...
return (...)
}
This should do the trick.
Note that I've used your sample API endpoint that you've provided in the comments, so you will have to change the handleResult callback inside of the handleChange handler to make sure you're parsing out your new data appropriately.
If you'd like to see a quick example, I created a CodeSandbox with a button that can be clicked to fetch more data and display it in a list:
Demo
Let me know if you have any questions.
Expected result: create fake API --> call function save--> method POST --> save object time to this.state.resul --> save db.json todos id 2
I'm using the library Fake an API with JSON Server.
db.json
{
"todos": [
{
"id": 1,
"title": "AAAA"
"results": [
{
"time": "09:10:01",
"desc": "bbbbb"
},
{
"desc": "gfgfgfgffg",
"time": "21:30:06"
}
]
},
{
"id": 2,
"title": "BBBBBBB",
"results": [
{
"time": "09:10:01",
"desc": "reerrererer",
},
{
"desc": "dfdfdfdfdfdfdfdfdfdfdf",
"time": "21:30:06"
}
]
}
}
Todos
class Todos extends Component {
constructor (props) {
super(props);
this.state = {
todos: []
}
}
componentDidMount() {
axios.get("http://localhost:3000/todos")
.then(response => {
this.setState({
todos: response.data
});
})
.catch(error => {
console.log('Error);
}
);
}
render () {
return(
)
}
}
export default Todos;
StopWatch
class StopWatch extends Component {
constructor() {
super();
this.state = {
resul: []
};
}
save = () => {
const resul = this.state.resul;
this.clock = {
clock: this.clock
}
resul.push(this.clock);
this.setState({
resul
});
axios.post(`http://localhost:4000/todos/2`, {
results: this.clock
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
render () {
return (
<ul className="results">
{
this.state.resul
.map((item, index) =>
<Time
key= {index}
/>
)
}
</ul>
);
}
}
export default StopWatch;
Use put to update the array, and pass the whole object you want to update.
save = () =>{
const resul = JSON.parse(JSON.stringify(this.state.resul));
this.clock = {
clock: this.clock
};
resul.push(this.clock);
this.setState({
resul
});
const todo = {
id: 2,
title: 'BBBBBBB',
results: resul
};
axios
.put(`http://localhost:4000/todos/2`, todo)
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
}