Struggling to get some data from api - reactjs

Hi i'm trying to get some data from "currency API" (https://exchangeratesapi.io/)
and this is code below
App.js
import React, { useEffect, useState, Fragment } from "react";
import Header from "./Header";
import Table from "./dashboard/Table";
import { getLatest } from "../actions/currencyAction";
import { useDispatch, useSelector } from "react-redux";
function App() {
const latest = useSelector(state => state.latest);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getLatest());
}, []);
return (
<Fragment>
<Header />
{console.log(latest.rates)}
</Fragment>
);
}
export default App;
currencyAction.js
import { GET_LATEST } from "../actionType";
import axios from "axios";
export function getLatest() {
return dispatch => {
return axios.get("https://api.exchangeratesapi.io/latest").then(res => {
dispatch({ type: GET_LATEST, payload: res.data });
});
};
}
I succesfully got the data from api
<Fragment>
<Header />
{console.log(latest.rates)}
</Fragment>
{CAD: 1.4372, HKD: 8.4324, ISK: 139.3, PHP: 55.248, DKK: 7.4699, …}
CAD: 1.4372
HKD: 8.4324
ISK: 139.3
PHP: 55.248
DKK: 7.4699
HUF: 337.61
CZK: 25.186
AUD: 1.6384
RON: 4.8063
SEK: 10.5833
IDR: 15098.95
INR: 77.8265
BRL: 4.7474
RUB: 70.6675
HRK: 7.465
JPY: 120.52
THB: 34.336
CHF: 1.06
SGD: 1.5164
PLN: 4.2989
BGN: 1.9558
TRY: 6.6599
CNY: 7.6102
NOK: 10.1328
NZD: 1.7095
ZAR: 16.3592
but when I'm trying acces each currency, Everthing collapse..
<Fragment>
<Header />
{console.log(latest.rates.CAD)}
</Fragment>
TypeError: Cannot read property 'CAD' of undefined
App
C:/Users/82102/cb/src/components/App.js:17
14 |
15 | return (
16 | <Fragment>
> 17 | <Header />
| ^ 18 | {console.log(latest.rates.CAD)}
19 | </Fragment>
20 | );
I struggled for whole day long but I just can't figure out why it's happening..
please share your knowledge.. thank you

You're trying to access a property on rates before it has been loaded from the API.
The easy solution is to check its existence first:
Make sure rates is truthy (since typeof null === 'object' is also true),
Check type to see if its an object,
Then access the property.
{latest.rates && typeof latest.rates === 'object' && console.log(latest.rates.CAD)}

Because you use console.log in return causes an error.
You should do the following:
import React, { useEffect, useState, Fragment } from "react";
import Header from "./Header";
import Table from "./dashboard/Table";
import { getLatest } from "../actions/currencyAction";
import { useDispatch, useSelector } from "react-redux";
function App() {
const latest = useSelector(state => state.latest);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getLatest());
}, []);
// add here
console.log(latest.rates)
return (
<Fragment>
<Header />
// remove below line
// {console.log(latest.rates)}
</Fragment>
);
}
export default App;

Related

Using TanStack Query with Child

i want to use query inside child object of Category but im gettin this error.
Unhandled Runtime Error Error:
QueryClientProvider to set one No QueryClient set, use QueryClientProvider to set one
pages/category/[pid].js
15 | heading: pid,
16 | };
> 17 | const { data, isLoading } = useQuery(["movieImage"], get);
| ^
18 | if (isLoading) {
19 | return <div>Loading...</div>;
20 | }
index.js
import Banner from '../components/Banner';
import Category from '../components/Category';
import LiveAuction from '../components/LiveAuction';
import TopCollectors from '../components/TopCollectors';
import TrendingNow from '../components/TrendingNow';
import { ReactQueryDevtools } from "react-query/devtools";
import {
QueryClient,
QueryClientProvider,
} from "react-query";
import { Fragment } from 'react';
const queryClient = new QueryClient();
const Home = () => {
return (
<Fragment>
{" "}
<QueryClientProvider client={queryClient} contextSharing={true}>
<Banner />
<Category />
<LiveAuction />
<TopCollectors />
<TrendingNow />
<ReactQueryDevtools initialIsOpen={true} />
</QueryClientProvider>
</Fragment>
);
}
export default Home
some part of pages/category/[pid].js
const { pid } = router.query;
const { data, isLoading } = useQuery(["movieImage"], getGenres);
if (isLoading) {
return <div>Loading...</div>;
}
tried to change ReactQueryDevtools position
added Fragment
added contextSharing={true} to QueryClientProvider

react test component based on conditional component in parent component with react testing library

I'm trying to test component that is wrapped with other component, the parent component has conditional rendering, for explanation:
import React, { FunctionComponent } from 'react'
import { getPermissions, ROLES, PERMISSIONS } from '~/constants/permissions'
import usePermission from '~/hooks/usePermission'
export interface UsePermissionParams {
as: ROLES;
children?: React.ReactNode;
permissions: PERMISSIONS[];
}
// This Component is used to check if the user has the permission to access Specific Component or part of the UI
const CanAccess: FunctionComponent<UsePermissionParams> = ({
as,
children,
permissions,
}) => {
const hasPermission = usePermission({ permissions })
if (as === ROLES.VENDOR) {
const hasAllPermissions = permissions?.every((permission) =>
getPermissions.vendor.includes(permission)
)
if (hasPermission && hasAllPermissions) {
return <>{children}</>
}
} else if (as === ROLES.ADMIN) {
const hasAllPermissions = permissions?.every((permission) =>
getPermissions.admin.includes(permission)
)
if (hasPermission && hasAllPermissions) {
return <>{children}</>
}
}
return <></>
}
export default CanAccess
this is the parent component that wrap the component that I need to test.
and this is the component that I want to test:
import React, { useContext, useState } from 'react'
import { CircularProgress } from '#mui/material'
import { useSelector } from 'react-redux'
import CanAccess from '~/components/CanAccess'
import CreateBox from '~/components/CreateBox'
import { ROLES, PERMISSIONS } from '~/constants/permissions'
import CardHolder from '~/modules/finance/Components/SponsoredProductsTab/Components/CardHolder'
import TopUpModal from '~/modules/finance/Components/TopUpModal'
import { FinanceContext } from '~/modules/finance/FinanceContext'
import * as styles from '~/modules/finance/styles'
import { State } from '~/store'
import { User } from '~/store/user/types'
import { i18n } from '~/translations/i18n'
import { currencySign } from '~/utils/getCurrencySign'
import { ls } from '~/utils/localStorage'
import { separator } from '~/utils/numberSeperator'
const AccountBalance = () => {
const { list, loading } = useContext<any>(FinanceContext)
const [showTopUpModal, setShowTopUpModal] = useState<boolean>(false)
const userRepresentation = useSelector<State, User | null>(
(state) => state.user.data
)
const targetSelected = ls.get('target_code')
const handleOpenTopUpModal = () => {
setShowTopUpModal(true)
}
const onClose = () => {
setShowTopUpModal(false)
}
const renderCreateTopUP = () => (
<CanAccess as={ROLES.VENDOR} permissions={[PERMISSIONS.FO_TOPUP_ACCESS]}>
<CreateBox
label="Top Up"
maxWidth="fit-content"
handleCreateCampaign={handleOpenTopUpModal}
/>
</CanAccess>
)
return (
<>
{showTopUpModal && (
<TopUpModal
onClose={onClose}
advertiserId={
userRepresentation == null
? 0
: userRepresentation?.advertiserId || 0
}
target={targetSelected}
open
/>
)}
<CardHolder
cardHeader="Your account balance:"
cardContent={`${currencySign() || ''} ${
separator(list?.balance) || '0'
}`}
CardBottom={renderCreateTopUP()}
/>
</>
)
}
export default AccountBalance
and this is the test file:
import AccountBalance from '.'
import { render, screen } from '~/test-utils'
describe('AccountBalance', () => {
it('should render the top up button', async () => {
render(<AccountBalance />)
const topUpBtn = await screen.findByText('Top up')
expect(topUpBtn).toBeInTheDocument()
})
})
and I have this error:
<body>
<div>
<div
class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 css-s9smhs-MuiPaper-root"
>
<div>
<p
class="MuiTypography-root MuiTypography-body1 css-1tvvmmg-MuiTypography-root"
>
Your account balance:
</p>
<p
class="MuiTypography-root MuiTypography-body1 css-1hd1hip-MuiTypography-root"
>
0
</p>
<div
class="css-0"
/>
</div>
</div>
</div>
</body>
5 | it('should render the top up button', async () => {
6 | render(<AccountBalance />)
> 7 | const topUpBtn = await screen.findByText('Top up')
| ^
8 | expect(topUpBtn).toBeInTheDocument()
9 | })
10 | })
the problem is: any component that is wrapped with the component CanAccess is not rendering and I have null
so what is the problem

Can't access state using 'useStoreState in react easy-peasy

I recently decided to learn about state management with easy-peasy, and followed along the basic tutorials, but i can't seem to access the state.
Here is the App component :-
import model from './model';
import Todo from './components/Todo.tsx';
import { StoreProvider, createStore } from 'easy-peasy';
const store = createStore(model);
function App() {
return (
<StoreProvider store={store}>
<div className="App">
<Todo />
</div>
</StoreProvider>
);
}
export default App;
Here is the model file 'model.js'
export default {
todos: [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
};
And this is the Todo file:-
import React from 'react';
import {useStoreState } from 'easy-peasy';
const Todo = () => {
//The line below does not work for me, when i do 'state.todos' i get an error that todos does not exist on type
const todos = useStoreState(state=>state.todos);
return (
<div>
</div>
);
}
export default Todo;
Try removing the .todos so that
const todos = useStoreState(state=>state.todos);
turns into:
const todos = useStoreState(state=>state);
import React from 'react'
import { useStoreState } from 'easy-peasy';
import Feed from './Feed'
const Home = ({isLoading,fetchError}) => {
const { searchResults} = useStoreState((state)=>state.searchResults)
return (
{isLoading && Loading Posts ...};
{fetchError && <p className='statusMsg' style={{ color: "red" }}>{ fetchError }};
{!isLoading && !fetchError && (searchResults.length ? : No posts to display)}
)
}
export default Home;

React - useContext is returning undefined

I trying to use React context to manage state in my project, but I cannot seem to figure out why it's returning undefined. I copied the example from another project I was working on, and it seems as if everything should be working properly. I just can't seem to pin down why it is not.
Here is where I am creating the context.
import React, { useState } from "react";
const initialTodos = [
{
id: 1,
title: "Setup development environment",
completed: true,
},
{
id: 2,
title: "Develop website and add content",
completed: false,
},
{
id: 3,
title: "Deploy to live server",
completed: false,
},
];
export const TodoContext = React.createContext({
todos: initialTodos,
onChange: () => {},
});
const TodoContextProvider = (props) => {
const [todos, setTodos] = useState(initialTodos);
const handleChange = () => {
console.log("clicked");
};
return (
<TodoContext.Provider value={{ todos: todos, onChange: handleChange }}>
{props.children}
</TodoContext.Provider>
);
};
export default TodoContextProvider;
Here is where I am wrapping the app.
import React from "react";
import ReactDOM from "react-dom";
import TodoContainer from "./components/TodoContainer";
import TodoContextProvider from "./context/TodoContext";
ReactDOM.render(
<TodoContextProvider>
<TodoContainer />
</TodoContextProvider>,
document.getElementById("root")
);
Here is my TodoContainer.
import React, { useContext } from "react";
import TodosList from "./TodosList";
import Header from "./Header";
import TodoContext from "../context/TodoContext";
const TodoContainer = (props) => {
const todoContext = useContext(TodoContext);
console.log("todoContext", todoContext);
return (
<div>
<Header />
<TodosList />
</div>
);
};
export default TodoContainer;
And here is where I am attempting to use the context.
import React, { useContext } from "react";
import TodoItem from "./TodoItem";
import TodoContext from "../context/TodoContext";
const TodosList = (props) => {
const todoContext = useContext(TodoContext);
console.log(todoContext);
return (
<div>
{todoContext.todos.map((todo) => (
<TodoItem key={todo.id} todo={todo.title} />
))}
</div>
);
};
export default TodosList;
Finally, here is the error I receive.
TypeError: Cannot read property 'todos' of undefined
TodosList
C:/Users/Stacey/repos/simple-todo-app/src/components/TodosList.js:11
8 | console.log(todoContext);
9 |
10 | return (
> 11 | <div>
| ^ 12 | {todoContext.todos.map((todo) => (
13 | <TodoItem key={todo.id} todo={todo.title} />
14 | ))}
You are importing the TodoContext as a default import but it must be a named import.
change in the TodosList.js:
import TodoContext from "../context/TodoContext";
to:
import { TodoContext } from "../context/TodoContext";
and it will work

Jest / Enzyme not recognizing props

I am trying to write a test for a React functional component that uses Redux and Hooks.
I am using Jest with Enzyme for testing.
For Reference:
This is the functional component being tested:
import React from 'react';
import {useDispatch, useSelector} from "react-redux";
import * as actions from '../../actions/actions';
import { Button, Icon } from "#material-ui/core";
export const EditBatchHeaderComponent = (props) => {
const dispatch = useDispatch();
const { selectedBatch } = props;
const { batchName } = selectedBatch;
return (
<div className="edit-header-container">
<Button disableRipple onClick={() => {dispatch(actions.unSelectBatch())} }>
<Icon>arrow_back</Icon>
</Button>
<span>Edit Batch</span>
<span>{batchName}</span>
</div>
);
};
This is component's container:
import React from 'react';
import { BatchHeaderComponent } from './BatchHeaderComponent';
import { BatchTableComponent } from './BatchTableComponent';
import { EditBatchComponent } from './EditBatchComponent';
import {useSelector} from "react-redux";
import {EditBatchHeaderComponent} from "./EditBatchHeaderComponent";
export const BatchManagementComponent = () => {
const { selectedBatch } = useSelector(state => state.batchManagementReducer);
if (selectedBatch.length) {
return (
<div className="component-container">
<EditBatchHeaderComponent selectedBatch={selectedBatch} />
<EditBatchComponent selectedBatch={selectedBatch} />
</div>
);
}
return (
<div className="component-container">
<BatchHeaderComponent />
<BatchTableComponent />
</div>
);
};
This is the default state of the reducer:
{
sorting: {
order: '',
orderBy: ''
},
searchBy: 'batchName',
searchText: '',
filterByStatus: '--',
filterByType: '--',
waiting: false,
batchData: [],
selectedBatch: {
batchName: '',
},
}
This is the test file that is failing to recognize the props:
import React from 'react';
import { EditBatchHeaderComponent } from '../../../components/batchManagement/EditBatchHeaderComponent';
import configureStore from '../../../store';
import {Provider} from "react-redux";
import Enzyme, { mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import {Button} from "#material-ui/core";
Enzyme.configure({ adapter: new Adapter() });
describe('EditBatchHeaderComponent', () => {
it('mounts to the DOM successfully', () => {
const wrapper = mount(<Provider store={configureStore()}>
<EditBatchHeaderComponent />
</Provider>);
expect(wrapper.find(EditBatchHeaderComponent)).toBeDefined();
});
it('deselects the account and closes when the back button is clicked', () => {
const props = {selectedBatch: {batchName: 'INFORM'}, dispatch: jest.fn()};
const obj = {};
const wrapper = mount(
<Provider store={configureStore()}>
<EditBatchHeaderComponent {...props} />
</Provider>
);
console.log(wrapper.find(EditBatchHeaderComponent).props());
wrapper.find(Button).first().simulate('click');
expect(wrapper.find(EditBatchHeaderComponent)).toEqual(obj);
});
});
This is the error text provided by the test suite:
FAIL src/spec/components/batchManagement/EditBatchHeaderComponent.test.js (7.182s)
● EditBatchHeaderComponent › mounts to the DOM successfully
TypeError: Cannot read property 'batchName' of undefined
8 | const dispatch = useDispatch();
9 | const { selectedBatch } = props;
> 10 | const { batchName } = selectedBatch;
| ^
11 | return (
12 | <div className="edit-header-container">
13 | <Button disableRipple onClick={() => {dispatch(actions.unSelectBatch())} }>
I have run a nearly identical test on a similar component that runs and covers the code appropriately.
I can't seem to figure out why the props aren't being recognized.
Any assistance would be greatly appreciated, thanks.

Resources