I encountered a problem when I try to fetch some data from PokeAPI. Here's my code for PokemonCard component.
import React, { useEffect, useState } from "react";
import axios from "axios";
const PokemonCard = ({ pokemonID }) => {
const [pokemon, setPokemon] = useState({});
useEffect(() => {
(async () => {
const result = await axios.get(
`http://pokeapi.co/api/v2/pokemon/${pokemonID + 1}`
);
setPokemon(result.data);
})();
// console.log(pokemon.weight)
}, [pokemonID]);
return (
<div className="pokemon">
{pokemon.sprites.front_default}
</div>
);
};
export default PokemonCard;
Everything works properly when I try to reach data like: pokemon.weight or pokemon.base_experience. But I get errors when I try to use some deeper nested variables.
pokemon.sprites.front_default gives me an error TypeError:
Cannot read property 'front_default' of undefined.
Here's a sample of data from API:
"name": "bulbasaur",
"order": 1,
"species": {
"name": "bulbasaur",
"url": "https://pokeapi.co/api/v2/pokemon-species/1/"
},
"sprites": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/1.png",
"back_female": null,
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/1.png",
"back_shiny_female": null,
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png",
"front_female": null,
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/1.png",
"front_shiny_female": null
},
"stats": [
{
"base_stat": 45,
"effort": 0,
"stat": {
"name": "hp",
"url": "https://pokeapi.co/api/v2/stat/1/"
}
}
],
"types": [
{
"slot": 2,
"type": {
"name": "poison",
"url": "https://pokeapi.co/api/v2/type/4/"
}
},
{
"slot": 1,
"type": {
"name": "grass",
"url": "https://pokeapi.co/api/v2/type/12/"
}
}
],
"weight": 69
PS. Is it a good practice to make about 150 separate calls to API in every child component? Or should I somehow do it with one call? Thank you.
You were trying to access a key inside an undefined key of pokemon variable. Please check the updated line where you are actually rendering.
{pokemon.sprites ? pokemon.sprites.front_default : ''}
As Pokemon is an empty object before the api fetches the data and
updates to the state, so pokemon.sprites is actually undefined.
import React, { useEffect, useState } from "react";
import axios from "axios";
const PokemonCard = ({ pokemonID }) => {
const [pokemon, setPokemon] = useState({});
useEffect(() => {
(async () => {
const result = await axios.get(
`http://pokeapi.co/api/v2/pokemon/${pokemonID + 1}`
);
setPokemon(result.data);
})();
// console.log(pokemon.weight)
}, [pokemonID]);
return (
<div className="pokemon">
//this should work for you
{pokemon.sprites ? pokemon.sprites.front_default : ''}
</div>
);
};
export default PokemonCard;
Gets much easier in Optional chaining (?.)
pokemon.sprites?.front_default
Related
the error in the console is :
ERROR in [eslint]
src\services\orders.js
Line 9:3: Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
app.jsx
import { useEffect, useState } from 'react';
import './App.css';
import { Table } from './components/Table';
import {ordersService} from './services/ordersService'
function App() {
const [orders , setOrders] = useState(null)
useEffect(() => {
async function fetchData() {
const ordersData = await ordersService.query();
console.log(ordersData);
}
fetchData();
}, []);
return (
<div className="app">
<Table orders={ orders } />
</div>
);
}
services\ordersService.js
export const ordersService = {
query
}
function query(sortBy) {
return Promise.resolve(gOrders)
}
const gOrders = [{
"order_ID": 2790846857303,
"name": "#38777",
"total_refunded": 0,
"created_at": "2020-11-23 23:27:52",
React Testing Using Mock service worker is returning undefined and then taking values from the actual API. As you can see from the image down below the test is getting passed by getting values from the actual API and the name ARLO WANG is getting pulled out. Where as the name I have kept in the mockResponse is "first last" in the handler.js file.
FollowersList.js
import React, { useEffect, useState } from 'react'
import "./FollowersList.css"
import axios from "axios"
import { Link } from 'react-router-dom';
import { v4 } from 'uuid';
export default function FollowersList() {
const [followers, setFollowers] = useState([]);
useEffect(() => {
fetchFollowers()
}, []);
const fetchFollowers = async () => {
const {data} = await axios.get("https://randomuser.me/api/?results=5")
setFollowers(data.results)
}
// console.log(followers)
return (
<div className="followerslist-container">
<div>
{followers.map((follower, index) => (
<div className="follower-item" key={v4()} data-testid={`follower-item-${index}`}>
<div className="followers-details">
<div className="follower-item-name">
<h4>{follower.name.first}</h4> <h4>{follower.name.last}</h4>
</div>
<p>{follower.login.username}</p>
</div>
</div>
))}
</div>
<div className="todo-footer">
<Link to="/">Go Back</Link>
</div>
</div>
)
}
FollowersList.test.js
import { render, screen } from "#testing-library/react";
import { BrowserRouter } from "react-router-dom";
import FollowersList from "../FollowersList";
const MockFollowersList = () => {
return (
<BrowserRouter>
<FollowersList />
</BrowserRouter>
);
};
describe("FollowersList Component", () => {
test("renders first follower", async () => {
render(<MockFollowersList />);
screen.debug()
expect(await screen.findByTestId("follower-item-0")).toBeInTheDocument();
});
});
src/mock/handler.js
import { rest } from 'msw';
const mockResponse = {
data: {
results: [
{
name: {
first: "first",
last: "last",
},
login: {
username: "x",
},
},
],
},
};
export const handlers = [
rest.get('https://randomuser.me/api/', (req, res, ctx) => {
return res(ctx.json({mockResponse}))
}
})
]
VSCODE terminal
Something is going wrong in the return line in handlers array in the handler file. It's not sending back the mockResponse correctly.
Found the mistake. The response structure returned by the actual API and the msw is different. Just had to remove the 'data' object in the mockResponse and keep the it just as an array of 'results'.
import { rest } from 'msw';
const mockResponse = {
results: [
{
name: {
first: "first",
last: "last",
},
login: {
username: "x",
},
},
],
};
export const handlers = [
rest.get('https://randomuser.me/api/', (req, res, ctx) => {
return res(ctx.json(mockResponse))
}
})
]
I have a local json which is returning all values on console.log so I know the path is correct. However when I am trying to pull an input value of name from it, it gives me error uni.map is not a function.
What am I'm missing, any help will be fab :).
import { useState } from 'react'
import axios from 'axios'
import Uni from './Uni'
function Unis() {
const [unis, setUnis] = useState([])
const [query, setQuery] = useState('')
function handleSearchQuery(e) {
e.preventDefault()
setQuery(e.target.value)
}
async function searchUnis(e) {
e.preventDefault()
console.log()
var response = await axios.get(data.json)
setUnis(response.data)
}
return (
<div>
<input value={query} onChange={handleSearchQuery} />
<button onClick={searchUnis}>Search</button>
<ul>
{
unis.map(function(i, index){
return (
<li key={index}>
<Uni name={i.name} />
</li>
)
})
}
</ul>
</div>
)
}
export default Unis;
data.json
{
"data": [
{
"name": "xxx",
"years": "2022"
},
{
"name": "hhh",
"years": "2021"
}
]
}
Good Luck,
You have used data.json directly without import.
var response = await axios.get(data.json)
Ans:
Step 1:
Import the data.json file in Unis file.
import Data from './data.json';
Step 2:
Use the data like below.
var response = await axios.get(Data.data);
Here is my code:
import Base from "./Base";
import axios from "axios";
import { createData } from "../../utils";
import { useState, useEffect } from "react";
export default function Today(props) {
const [scoops, setScoops] = useState(0);
//Fetch api/scoops/today
const fetchScoops = async () => {
const res = await axios.get("http://localhost:8000/api/scoops/today/");
setScoops(res.data);
};
useEffect(() => {
fetchScoops();
}, []);
console.log(scoops[0]);
const rows = [
createData(0, 1, scoops[0].title, "http://example.com"),
createData(1, 2, "Paul McCartney", "http://example.com"),
createData(2, 3, "Tom Scholz", "http://example.com"),
createData(3, 4, "Michael Jackson", "http://example.com"),
createData(4, 5, "Bruce Springsteen", "http://example.com"),
];
return <Base rows={rows} duration="Today" />;
}
Here is what the console returns:
> undefined
> Today.js:20 {url: 'http://localhost:8000/api/scoops/1/', title: 'Hello World!', rank: 0, created_at: '2021-10-05T04:44:52.027336Z', updated_at: '2021-10-05T04:44:52.027336Z'}
The problem is when I refresh the page, I get the following error message:
TypeError: Cannot read properties of undefined (reading 'title')
Help would be much appreciated!
Update:
Optional chaining solved the issue, this works great.
scoops[0]?.title
You can set the initial scoops state to []
const [scoops, setScoops] = useState([]);
render scoops when data fetched using conditionalRendering
return <> {scoops.lenght > 0 && <Base rows={rows} duration="Today" />} </>;
The problem is, you are initializing scoops as 0 but using it like an array: scoops[0]
Try initializing scoops as an empty array. So something like this should work:
const [scoops, setScoops] = useState([]);
Also, where you are doing this: scoops[0].title you should instead use Optional chaining and use scoops[0]?.title
You should use Optional chaining (?.)
scoops?.[0]
Hello Everyone!
In the WatermelonDB docs you can make your component reactive with withObservables() method, it will update or re-render your component every time the data changes.
In my code
I have basic user data that has been stored to WatermelonDB, and the result when I am console.log() that data is:
{
"__changes": {
"_isScalar": false,
"_value": [Circular],
"closed": false,
"hasError": false,
"isStopped": false,
"observers": [],
"thrownError": null
},
"_hasPendingDelete": false,
"_hasPendingUpdate": false,
"_isCommitted": true,
"_isEditing": false,
"_raw": {
"_changed": "",
"_status": "created",
"city": "NY City", // The data that I added
"id": "rddcv3ttt9s03jel", // The data that I added
"name": "John Doe", // The data that I added
"user_id": 0 // The data that I added
},
"_subscribers": [],
"collection": {
"_cache": {
"map": [Map],
"recordInsantiator": [Function anonymous],
"tableName": "user"
},
"_subscribers": [],
"changes": {
"_isScalar": false,
"closed": false,
"hasError": false,
"isStopped": false,
"observers": [Array],
"thrownError": null
},
"database": {
"_actionQueue": [ActionQueue],
"_actionsEnabled": true,
"_isBeingReset": false,
"_resetCount": 0,
"_subscribers": [Array],
"adapter": [DatabaseAdapterCompat],
"collections": [CollectionMap],
"schema": [Object]
},
"modelClass": [Function User]
}
}
With withObservables() method I can display that data, and here's my code:
import React, {useEffect, useState} from 'react';
import {Button, Layout} from '#ui-kitten/components';
import WATERMELON from '../models';
import util from '../utils';
import {View} from 'react-native';
import withObservables from '#nozbe/with-observables';
import {Text} from '../components/Helper';
const enhance = withObservables(['user'], props => {
return {
user: props.user,
};
});
const UserBasicInfo = enhance(props => {
// Successfully displaying the data but not Reactive
return (
<>
<Text>{props.user.name}</Text> {/* John Doe */}
<Text>{props.user.city}</Text> {/* NY City */}
</>
);
});
const TestScreen = props => {
const [user, setUser] = useState(null);
useEffect(() => {
(async () => {
await WATERMELON.action(async () => {
const user_collection = WATERMELON.collections.get('user');
const fetch_userdata = await user_collection.find('rddcv3ttt9s03jel');
console.log(fetch_userdata);
setUser([fetch_userdata]);
});
})();
}, []);
return (
<Layout>
<View>
<Text>Hello Test Screen!</Text>
{user !== null && <UserBasicInfo user={user} />}
<Button
onPress={async () => {
await WATERMELON.action(async () => {
const user_collection = WATERMELON.collections.get('user');
const userd = await user_collection.find('rddcv3ttt9s03jel');
await userd.update(user => {
// Just want to change the city
// And it's just fine BUT not reactive :(
user.city = 'Chicago';
});
});
}}
>
Press It
</Button>
</View>
</Layout>
);
};
export default TestScreen;
My models/user.model.js
import {Model} from '#nozbe/watermelondb';
import {field} from '#nozbe/watermelondb/decorators';
export default class User extends Model {
static table = 'user';
#field('name') name;
#field('city') city;
}
My models/index.js file
import fs from 'react-native-fs';
import {Database} from '#nozbe/watermelondb';
import SQLiteAdapter from '#nozbe/watermelondb/adapters/sqlite';
import schema from '../schema/watermelon.schema';
import userModel from './user.model';
import customerModel from './customer.model';
// First, create the adapter to the underlying database:
const adapter = new SQLiteAdapter({
schema,
dbName: `${fs.DocumentDirectoryPath}/restronic`, // optional database name or file system path
// migrations, // optional migrations
synchronous: true, // synchronous mode only works on iOS. improves performance and reduces glitches in most cases, but also has some downsides - test with and without it
// experimentalUseJSI: true, // experimental JSI mode, use only if you're brave
});
// Then, make a Watermelon database from it!
const db = new Database({
adapter,
modelClasses: [userModel, customerModel],
actionsEnabled: true,
});
export default db;
I dunno what's wrong with my code, I just follow the tutorial in the Docs but it's still not reactive :(
Update
I try with .subscribe() method and still not reactive and even worse it's not re-rendering the component when I setState() inside that method, The method is running well when I am trying console.log() inside that method
const UserBasicInfo = props => {
const [state, setState] = useState(props.user);
useEffect(() => {
const subscription = props.user.subscribe(newVal => {
// Not re-rendering the component
setState(newVal);
});
return () => subscription.unsubscribe();
});
return <>{state !== null && <Text>{state.name}</Text>}</>;
};
Add 'observe()' in the end, it will work
const enhance = withObservables(['user'], props => {
return {
user: props.user.observe(),
};
});