It's be a while since I was doing any sort of coding and I am coming back to help a friend with the creation of a site. However jumping straight into the depend I am now stuck with SWR.
Problem
I am just trying to send a Authorization: Bearer header however upon the page loading SWR just shows it loading state? I am aware this could be a simple problem but a helpful answer would be amazing. I have also tried looking at dev tools etc and nothing related to SWR is there.
The API Site is built with Strapi V4 and I am aware they have changed there structre of the API response which could also be an issue for me however I am not aware of one yet, so if you spot one gladly let me know!
Code
import {
Table,
TableCaption,
Thead,
Tr,
Th,
Tbody,
Td,
Tfoot,
} from "#chakra-ui/react";
import useSWR from "swr";
import axios from "axios";
const fetcher = async (url) => {
const res = await axios.get(url, {
headers: {
"Authorization": `Bearer ${process.env.API_KEY}`,
},
});
return res.data;
};
export const BluForTable = () => {
const { data, error} = useSWR(
["https://api-skr.herokuapp.com/api/factions", fetcher]
);
if(error) return (
console.log(error),
(
<div>Failed to load {error.message}</div>
)
)
if(!data) return <div>loading...</div>
return (
<>
<div>
{data.map((faction) => (
<Text>
{faction.data.attributes.Title}
</Text>
))}
</div>
<Table variant="simple">
<TableCaption>Imperial to metric conversion factors</TableCaption>
<Thead>
<Tr>
<Th>To convert</Th>
<Th>into</Th>
<Th isNumeric>multiply by</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>inches</Td>
<Td>millimetres (mm)</Td>
<Td isNumeric>25.4</Td>
</Tr>
<Tr>
<Td>feet</Td>
<Td>centimetres (cm)</Td>
<Td isNumeric>30.48</Td>
</Tr>
<Tr>
<Td>yards</Td>
<Td>metres (m)</Td>
<Td isNumeric>0.91444</Td>
</Tr>
</Tbody>
<Tfoot>
<Tr>
<Th>To convert</Th>
<Th>into</Th>
<Th isNumeric>multiply by</Th>
</Tr>
</Tfoot>
</Table>
</>
);
};
API Response
{
"data": [
{
"id": 1,
"attributes": {
"Title": "Russia",
"faction_information": "# Russian Squad Faction\n",
"faction_lunch_date": "2015-02-28",
"createdAt": "2022-01-28T14:42:02.087Z",
"updatedAt": "2022-01-28T14:46:02.807Z",
"publishedAt": "2022-01-28T14:46:02.804Z",
"media_link": null
}
},
{
"id": 2,
"attributes": {
"Title": "US",
"faction_information": "# whatever",
"faction_lunch_date": null,
"createdAt": "2022-01-29T04:37:36.773Z",
"updatedAt": "2022-01-29T04:38:23.549Z",
"publishedAt": "2022-01-29T04:37:48.962Z",
"media_link": null
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 2
}
}
}
You have an error when passing a key, here:
const { data, error} = useSWR(
["https://api-skr.herokuapp.com/api/factions", fetcher]
);
First argument of useSWR function is the key, second is the fetcher. You pass them as an array and useSWR thinks that this whole thing is a composite key (which is possible to have in advanced scenarios)
So just remove array brackets and pass key and fetcher as separate arguments:
const { data, error} = useSWR(
"https://api-skr.herokuapp.com/api/factions", fetcher
);
Related
I am creating a functional component am fetching some data from an internal API and am wondering how I can make destructure the table rows into something a little less verbose. The following is the response I am getting from this API.
{
"data": [
{
"id": "cc134653-c463-4e79-8b9e-f52dfe02498e",
"type": "bottle",
"attributes": {
"name": "Luc Belaire Rare Luxe",
"price": "$29.99",
"image": "https://cdn11.bigcommerce.com/s-
7a906/images/stencil/1000x1000/products/10929/10518/Luc-Belaire-Rare-Luxe__73902.1555086630.jpg?c=2",
"sku": "813497005010",
"size": "750ML",
"origination": "France",
"varietal": "Sparkling Wine"
}
},
}
I am setting the state of the component like this.
const [bottles, setBottles] = useState([]);
useEffect(() => {
fetch('http://localhost:3000/api/v1/bottles?', { method: "GET" })
.then(response => response.json())
.then(data => setBottles(data.data));
});
This is how I am creating a table body in my component but am wondering if there is a better way to use the bottle.attributes.name and other attributes to make it more like {name}. How can I achieve this?
<tbody>
{bottles.map(bottle =>
<tr key={bottle.id}>
<td><img src={bottle.attributes.image} alt={bottle.attributes.name} height={150} width={100}/></td>
<td>{bottle.attributes.name}</td>
<td>{bottle.attributes.sku}</td>
<td>{bottle.attributes.price}</td>
<td>{bottle.attributes.size}</td>
<td>{bottle.attributes.origination}</td>
<td>{bottle.attributes.varietal}</td>
</tr>
)}
</tbody>
It will have to be a bit repetitive regardless - if you destructure the argument, you'll have to list out each individual property in the argument list:
{bottles.map(({ id, attributes: { image, name, sku, price, size, origination, varietal }}) =>
<tr key={id}>
<td><img src={image} alt={name} height={150} width={100}/></td>
<td>{name}</td>
<td>{sku}</td>
I'd prefer to just destructure to get the attributes, and then list attributes.name, etc:
<tbody>
{bottles.map(({ id, attributes }) =>
<tr key={id}>
<td><img src={attributes.image} alt={attributes.name} height={150} width={100}/></td>
<td>{attributes.name}</td>
<td>{attributes.sku}</td>
which is better than going through bottle each time.
You could make an array of the property names and use an inner map()
const attrs =['name', 'sku', 'price', 'size', 'origination', 'varietal']
<tbody>
{bottles.map(bottle =>
<tr key={bottle.id}>
<td><img src={bottle.attributes.image} alt={bottle.attributes.name} height={150} width={100}/></td>
{attrs.map(k => <td>{bottle.attributes[k]}</td>}
</tr>
)}
</tbody>
I am trying to render an object held in state. The contents of this.state is:
"statement": {
"creator": "8ff243b2-f21e-43f3-9090-4aa679fbeb1a",
"id": "bf4c965e-bd59-48c8-b31a-8f67529e5fde",
"impactors": [
"978388e8-2987-4c89-82b6-4da619d82935",
"1d75e2a7-bf2a-4f55-ba68-373752b24f98"
],
"score": {
"impactors": 2,
"net": 3,
"percentage": 0.75,
"total_votes": 4
},
"statement": "The Earth is round."
}
In my React JS app, I am able to render some of the child object (statement) but not all. Here is my code:
renderStatement() {
return (
<tbody>
<tr>
<td>Statement: </td>
<td>{this.state.statement.score}</td>
</tr>
</tbody>
)
}
As expected, the above code returns an error: Error: Objects are not valid as a React child (found: object with keys {impactors, net, percentage, total_votes}). If you meant to render a collection of children, use an array instead.
What I actually want is percentage, which is a node underneath score. When I try to drill down and have it just render percentage, like this:
renderStatement() {
return (
<tbody>
<tr>
<td>Statement: </td>
<td>{this.state.statement.score.percentage}</td>
</tr>
</tbody>
)
}
I get the following error: TypeError: Cannot read property 'percentage' of undefined.
Any of the other objects ('creator', 'id', 'impactors', 'statement') work just fine.
This is how I fixed it:
renderStatement() {
const score = this.state.statement.score || {}
return (
<tbody>
<tr>
<td>Statement: </td>
<td>{score.percentage}</td>
</tr>
</tbody>
)
}
you can do the following:
renderStatement() {
const text = (typeof this.state.statement.score !== "undifined") ?
this.state.statement.score.percentage : ''
return (
<tbody>
<tr>
<td>Statement: </td>
<td>{text}</td>
</tr>
</tbody>
)
}
This question already has answers here:
React JS - Uncaught TypeError: this.props.data.map is not a function
(17 answers)
Closed 3 years ago.
I am new to React and it gives me the error below, which I cannot resolve. I searched in the internet, a lot of people faced the same problem but "map" is a built in function, why does it say that it is not a function?
import React, { Component } from 'react';
export class UrunlerComponentList extends Component {
displayName = UrunlerComponentList.name
constructor(props) {
super(props);
this.state = { urunler_: [], loading: true };
fetch('http://localhost:55992/api/search/arama?q=&PageIndex=1')
.then(response => response.json())
.then(data => {
this.setState({ urunler_: data, loading: false });
});
}
static renderUrunlerTable(urunler_) {
return (
<table className='table'>
<thead>
<tr>
<th>SKU</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{urunler_.map(urun =>
<tr>
<td>{urun.SKU}</td>
<td>{urun.Name}</td>
</tr>
)}
</tbody>
</table>
);
}
render() {
let contents = this.state.loading
? <p><em>Loading...</em></p>
: UrunlerComponentList.renderUrunlerTable(this.state.urunler_);
return (
<div>
<h1>Urunler</h1>
<p>This component demonstrates fetching data from the server.</p>
{contents}
</div>
);
}
}
The webapi I am calling returns the value below...
{
"$id": "1",
"RecordCount": 879,
"products": [
{
"$id": "2",
"Id": 17034,
"BrandId": 3,
"SKU": "7436B003-1082",
"Name": "xxxxxxxx",
"InStock": 1,
"URL": "xxxxxxxxxx",
"Description": "xxxxxxxxxxxxxxxxx",
"Price": 9.90,
"DiscountRatio": 0.00,
"TechnicalDetail": "xxxxxxxxxxxxxxxxx",
"ImageUrl": "7436Bxxxxxx.jpg",
"Active": true,
"CreatedDate": "2019-07-20T11:36:35.333",
"Brand": null,
"CartDetails": [],
"OrderDetails": [],
"ProductCategoryRelations": [],
"ProductColorRelations": [],
"ProductFileRelations": [],
"ProductPropertyRelations": []
},
This should fix it. Data itself is an object not an array. You want to assign products I guess.
...
.then(({products}) => this.setState({ urunler_: products, loading: false }))
...
Make sure the value returned from your fetch call is an array and is not undefined since you are setting that value to your state.
fetch('http://localhost:55992/api/search/arama?q=&PageIndex=1')
.then(response => response.json())
.then(data => {
// make sure 'data' is an array
this.setState({ urunler_: data, loading: false });
});
<tbody>
{typeof urunler_ !== 'undefined' && urunler_.length > 0 ?
(urunler_.map(urun =>
<tr>
<td>{urun.SKU}</td>
<td>{urun.Name}</td>
</tr>
)):(<tr><</tr>)}
</tbody>
This question already has an answer here:
Why doesn't my arrow function return a value?
(1 answer)
Closed 3 years ago.
My issue similar to this, but not the same.
I'm trying to sort an array of objects using localeCompare. It has no reason not to work, but it's not working. Somehow I'm doing something right./sigh/
This is how I fetch the data
const [data, setData] = useState([]);
const [isBusy, setBusy] = useState(true)
useEffect(() => {
console.log('I was run once');
async function fetchData() {
const url = `${
process.env.REACT_APP_API_BASE
}/api/v1/endpoint/`;
axios.get(url).then((response: any) => {
setBusy(false);
setData(response.data.results)
console.log(response.data.results);
});
}
fetchData();
}, [])
This is the sorting function
function onSort(event: any, sortKey: string) {
const newData = data;
newData.sort((a: any, b: any): any => {
a[sortKey].toString().localeCompare(b[sortKey]);
})
setData(newData);
}
A typical array of objects is like this
[
{
"avatar": "big",
"name": "Username",
},
{
"avatar": "small",
"name": "Rex",
},
{
"avatar": "medium",
"name": "Duh",
}
]
Then the table header has this
<th scope="col" className="text-center" onClick={e => onSort(e, 'name')}>Name</th>
Although the onSort() function runs without any errors, the objects don't get sorted as per the sortKey
Am I using the localeCompare wrongly? What can I do to make the above right?
Also, the setData, I believe should update the data and thus trigger a rebuild of the table, which seems to be happening, because if I pass in an empty array to the setData, the table blanks.
edit:
The HTML part
...
return (
<div>
<table className="table table-hover">
<thead>
<tr>
<th onClick={e => onSort(e, 'avatar')}>Avatar</th>
<th onClick={e => onSort(e, 'name')}>Name</th>
</thead>
<tbody>
{data && data.map((item: any, index: any) => {
return (
<tr key={index}>
<th>{{ item.avatar}}</th>
<td>{item.name}</td>
<td className="text-center" style={{ 'fontSize': '32px', 'color': '#981c1e' }}>0</td>
</tr>
)
})}
</tbody>
</table>
</div>
...
If I understand, if the setData triggers a data change, the render re-renders?
You need a return statement by using curly brackets in an arrow function
newData.sort((a: any, b: any): any => {
return a[sortKey].toString().localeCompare(b[sortKey]);
});
I am getting "Uncaught TypeError: this.state.data.map is not a function" error even I can successfully log data to console from the API.
I have found similar questions but, haven't come up with a good solution to solve this issue yet.
I have read here that, "Objects, {}, in JavaScript does not have the method .map(), it's only for Arrays, []."
However, I can not figure out how to fix this issue, iterate over an object and retrieve data to the React front end too.
Thank you and any help would be greatly appreciated.
import React, { Component } from "react";
import axios from "axios";
export default class GetSubjects extends Component {
constructor(props) {
super(props);
this.getsubjects = this.getsubjects.bind(this);
this.onSearch = this.onSearch.bind(this);
this.state = {
keyword: "",
data: []
};
}
getsubjects(e) {
this.setState({ keyword: e.target.value });
}
onSearch(e) {
e.preventDefault();
const searchsub = {
keyword: this.state.keyword
};
axios
.get(`http://localhost:3000/api/messages/courses/${this.state.keyword}`)
.then(response => {
console.log(response);
this.setState({
data: response.data
});
});
console.log(this.state.keyword);
console.log(this.state.data);
}
componentDidMount() {}
render() {
return (
<div>
<br />
<div>
<label>Course Name</label>{" "}
<input
placeholder="Enter Course Name"
type="text"
value={this.state.keyword}
onChange={this.getsubjects}
name="keyword"
required
/>{" "}
<button className="btn btn-primary" onClick={this.onSearch}>
Get Subjects
</button>
</div>
<br />
<table className="table table-bordered">
<thead>
<tr>
<th scope="col">Course Name</th>
<th scope="col">Subjects</th>
</tr>
</thead>
<tbody>
{this.state.data.map(function(subject) {
return (
<tr>
{" "}
<td key={subject.id}>{subject.name}</td>{" "}
<td key={subject.id}>{subject.subjects}</td>{" "}
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
You've said what you're receiving is:
{
"subjects": ["Computer Architecture", "Basic Networking"],
"_id": "5cf368bfb58f8c35bc19cebc",
"name": "Software Engineering",
"passmark": 75,
"lectureIncharge": "John Smith",
"__v": 0
}
Your setState call sets data to that object, which is not an array.
Your render code expects that to be an array, though. It tries to loop through it and use it to fill in rows in a table. But you're only getting one piece of data: A single course ("Software Engineering") which covers two subjects ("Computer Architecture" and "Basic Networking").
So your render code shouldn't be trying to use that as an array (and so you may not want a table anymore). It should just use data's name and subjects properties directly.
I'll keep your table markup for now, but note that this only produces a single row (because there's only one piece of data). Prior to the return, I'd grab the course:
const course = this.state.data;
then where you're outputting your table:
<tbody>
{course && course.name
? undefined
:
<tr>
<td key={course.id}>{course.name}</td>{" "}
<td key={course.id}>{course.subjects.join(", ")}</td>{" "}
</tr>
}
</tbody>
I've used an explicit join(", ") to join the subjects rather than implicitly via toString, which wouldn't include a space after the comma.
response.data is an object which axios fills when the request is done.
It's true you're initializing data as an array, but when you do:
this.setState({ data: response.data }); You've actually changed it to an object.
Parse the object you get to an array or do something else with the returned data.
Edit: After you response: just do: this.setState({data: response.data.subject});
//yo lo solucione poniendo data.data
//porque (data) solo me devolvĂa un objeto con varios atributos y data era uno de esos
//entonces le agregue data.data.
getCategorias(){
axios.get("http://localhost:8000/api/categorias")
.then(data => {
this.setState({
data: data.data
});
}).catch((error)=>{
console.error(error)
});
}
componentDidMount(){
this.getCategorias();
}