I am trying to build an eCommerce website to learn Redux.
At the moment I am trying to fetch the categories when the component mounts. Since I am using functional components, I understood that this is achieved by calling the useEffect() method.
Also, I am using json-server as a REST Api.
I am quite sure I have managed to compose my enhancers to pass to the store (dev tools and thunk), created actions, reducers and all.
My problem is that the action doesn't fire when the component mounts.
N.B. before introducing the Middleware and therefore the fetch request, everything worked just fine. Also consider that the fetch request is successful.
Hereafter is the code involved.
'src/index.js'
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore, applyMiddleware, compose } from 'redux'
import { Provider } from 'react-redux'
import rootReducer from './reducers'
import thunk from 'redux-thunk'
const composedEnhancers = compose(applyMiddleware(thunk), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
const store = createStore(
rootReducer, /* preloadedState, */
composedEnhancers
);
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
the action type in 'actions/index.js':
export const FETCH_CATEGORIES = 'FETCH_CATEGORIES'
the action creator in 'actions/index.js':
export const fetchCategories = () => (dispatch) => {
fetch("http://localhost:7000/categories")
.then(response => response.json())
.then(categories => {
return {
type: FETCH_CATEGORIES,
payload: categories
}
})
}
'reducers/index.js'
import * as actions from './../actions'
const initState = {
categories: [],
currentCategory: 'any',
toggler: 'hidden'
}
const rootReducer = (state = initState, action) => {
switch (action.type) {
case actions.SELECT_CATEGORY:
return { ...state, currentCategory: action.payload.value }
case actions.FETCH_CATEGORIES:
return { ...state, categories: action.payload }
case actions.TOGGLE:
return { ...state, toggler: action.payload.toggler }
default:
return state
}
}
export default rootReducer
the 'Filter.js' component
import React, { useEffect } from 'react';
import { connect } from 'react-redux'
import { selectCategory, fetchCategories } from '../../../../actions'
const Filter = (props) => {
// const [minPrice, setMinPrice] = useState(0)
// const handleMinPrice = event => {
// setMinPrice(event.target.value)
// }
// const [maxPrice, setMaxPrice] = useState(0)
// const handleMaxPrice = event => {
// setMaxPrice(event.target.value)
// }
// const [department, setDepartment] = useState("select")
// const handleDepartment = event => {
// console.log(event.target.value)
// setDepartment(event.target.value)
// }
// console.log(props);
const handleChange = event => {
event.preventDefault()
props.selectCategory(event.target.value)
}
useEffect(() => {
props.fetchCategories()
})
return (
<div className="filter-form col-12">
<form id="filter-category">
<label htmlFor="category">Category</label>
<select className="col-12" id="category" name="category" size="5" value={props.currentCategory} onChange={(event) => handleChange(event)}>
{props.categories.map(category => <option key={category.value} value={category.value}>{category.name}</option>)}
</select>
</form>
{props.currentCategory !== 'any' && <form id="filter-department">
<label htmlFor="department">Department</label>
<select className="col-12" id="department" name="department" size="5" value='{department}' onChange='{handleDepartment}'>
<option value="select">--- Select ---</option>
<option value="desktop PCs">Desktop PCs</option>
<option value="laptops">Laptops</option>
<option value="gamepads">Gamepads</option>
<option value="headphones">Headphones</option>
<option value="microphones">Microphones</option>
<option value="keyboards">Keyboards</option>
</select>
</form>}
{/* <form id="filter-price">
<label htmlFor="minimum-price">Min. Price: {minPrice}£</label>
<input type="range" min="1" max="100" value={minPrice} className="slider col-xs-12" id="minimum-price" onChange={handleMinPrice} />
<label htmlFor="maximum-price">Max. Price: {maxPrice}£</label>
<input type="range" min="100" max="1000" value={maxPrice} className="slider col-xs-12" id="maximum-price" onChange={handleMaxPrice} />
</form> */}
</div>
);
}
const mapStateToProps = (state) => {
return {
categories: state.categories,
currentCategory: state.currentCategory
}
}
const mapDispatchToProps = (dispatch) => {
return {
selectCategory: (value) => {
dispatch(selectCategory(value))
},
fetchCategories: () => {
dispatch(fetchCategories())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Filter);
Also, here is 'db.json'
{
"categories": [
{
"id": "1",
"value": "any",
"name": "--- Any ---",
"departments": []
},
{
"id": "2",
"value": "computers-and-accessories",
"name": "Computers and Accessories",
"departments": [
{
"id": "1",
"value": "desktop-pc",
"name": "Desktop PCs"
},
{
"id": "2",
"value": "laptops",
"name": "Laptops"
},
{
"id": "3",
"value": "keyboards",
"name": "Keyboards"
},
{
"id": "4",
"value": "headphones",
"name": "Headphones"
},
{
"id": "5",
"value": "mouses",
"name": "Mouses"
},
{
"id": "6",
"value": "gamepads",
"name": "Gamepads"
}
]
},
{
"id": "3",
"value": "fashion",
"name": "Fashion",
"departments": [
{
"id": "1",
"value": "dresses",
"name": "dresses"
},
{
"id": "2",
"value": "shoes",
"name": "Shoes"
},
{
"id": "3",
"value": "pants",
"name": "Pants"
},
{
"id": "4",
"value": "sunglasses",
"name": "Sunglasses"
},
{
"id": "5",
"value": "handbags",
"name": "Handbags"
},
{
"id": "6",
"value": "hats",
"name": "Hats"
}
]
},
{
"id": "4",
"value": "digital-music",
"name": "Digital Music",
"departments": [
{
"id": "1",
"value": "rock",
"name": "Rock"
},
{
"id": "2",
"value": "pop",
"name": "Pop"
},
{
"id": "3",
"value": "house-and-techno",
"name": "House and Techno"
},
{
"id": "4",
"value": "trap",
"name": "Trap"
},
{
"id": "5",
"value": "indie",
"name": "Indie"
},
{
"id": "6",
"value": "hip-hop",
"name": "Hip-Hop"
}
]
},
{
"id": "5",
"value": "house",
"name": "House",
"departments": [
{
"id": "1",
"value": "kitchen",
"name": "kitchen"
},
{
"id": "2",
"value": "garden",
"name": "Garden"
},
{
"id": "3",
"value": "bedroom",
"name": "Bedroom"
},
{
"id": "4",
"value": "bathroom",
"name": "Bathroom"
},
{
"id": "5",
"value": "livingroom",
"name": "Livingroom"
},
{
"id": "6",
"value": "cleaning",
"name": "Cleaning"
}
]
},
{
"id": "6",
"value": "grocery",
"name": "Grocery",
"departments": [
{
"id": "1",
"value": "vegetables",
"name": "Vegetables"
},
{
"id": "2",
"value": "pasta and rice",
"name": "Pasta and Rice"
},
{
"id": "3",
"value": "snacks",
"name": "Snacks"
},
{
"id": "4",
"value": "canned-food",
"name": "Canned Food"
},
{
"id": "5",
"value": "frozen",
"name": "Frozen"
},
{
"id": "6",
"value": "dairy",
"name": "Dairy"
}
]
}
]
}
What am I missing here?
Try dispatching like this in the action which will fire the reducer FETCH_CATEGORIES:
export const fetchCategories = () => (dispatch) => {
fetch("http://localhost:7000/categories")
.then(response => response.json())
.then(categories => {
// **Changes start here
dispatch ({
type: FETCH_CATEGORIES,
payload: categories
})
// **Changes end here
})
}
Related
TypeError: Cannot read properties of undefined (reading 'forEach')
This is an error message I get when I am trying to render OrderTable component. I struggled fixing it for a good few hours and I don't have idea what is wrong.
Other almost similar tables works fine.
const orderProducts = useSelector((state) => state.orders.order.products)
I use this structure instead of state.orders.order because from serializer I got nested data that does not works. Tried to take products from order after selector and it does not also work. Data seems to be fine, but I attach JSON anyway.
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getOrder } from "../../features/orders/orderSlice";
import {
useTable,
useRowSelect,
useSortBy,
useFilters,
useGlobalFilter,
} from "react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from "#mui/material";
import Paper from "#mui/material/Paper";
import KeyboardArrowUpIcon from "#mui/icons-material/KeyboardArrowUp";
import KeyboardArrowDownIcon from "#mui/icons-material/KeyboardArrowDown";
import TableContainer from "#mui/material/TableContainer";
import IndeterminateCheckbox from "../Checkbox";
import ColumnFilter from "../ColumnFilter";
// import GlobalFilter from "../GlobalFilter";
const OrderTable = () => {
const dispatch = useDispatch();
useEffect(() => {
const id = window.location.pathname.split("/orders/order/").pop();
dispatch(getOrder(id));
}, []);
const loading = useSelector((state) => state.orders.loading);
const orderProducts = useSelector((state) => state.orders.order.products);
const data = React.useMemo(() => orderProducts, [orderProducts]);
const columns = React.useMemo(
() => [
{ Header: "ID", accessor: "id", Filter: ColumnFilter },
{ Header: "Nazwa", accessor: "product.title", Filter: ColumnFilter },
{ Header: "Ilość", accessor: "quantity", Filter: ColumnFilter },
{ Header: "Cena", accessor: "product.price", Filter: ColumnFilter },
{ Header: "Wartość", accessor: "total_price", Filter: ColumnFilter },
{
Header: "Zamówiony",
accessor: (d) => (d.ordered ? "Tak" : "Nie"),
Filter: ColumnFilter,
},
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
state,
setGlobalFilter,
prepareRow,
selectedFlatRows,
} = useTable(
{ columns, data },
useGlobalFilter,
useFilters,
useSortBy,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]);
}
);
const { globalFilter } = state;
return (
<>
{loading ? (
"loading"
) : (
<TableContainer component={Paper} sx={{ ml: 3, mt: 2 }}>
<Table {...getTableProps()} size="small">
<TableHead>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableCell {...column.getHeaderProps()}>
<div {...column.getSortByToggleProps()}>
{column.render("Header")}
{column.isSorted ? (
column.isSortedDesc ? (
<KeyboardArrowUpIcon fontSize="small" />
) : (
<KeyboardArrowDownIcon fontSize="small" />
)
) : (
""
)}
</div>
<div>
{column.canFilter ? column.render("Filter") : null}
</div>
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
return (
<TableRow {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<TableCell {...cell.getCellProps()}>
{cell.render("Cell")}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
)}
</>
);
};
export default OrderTable;
JSON
{
"id": 37,
"products": [
{
"id": 68,
"product": {
"id": 1,
"created": "2021-11-11T02:01:15.897446Z",
"active": true,
"title": "Kapustka",
"description": "biała",
"quantity": -143,
"price": "3.24",
"category": 1
},
"quantity": 8,
"total_price": "25.92",
"ordered": true,
"user": 1
},
{
"id": 69,
"product": {
"id": 2,
"created": "2021-11-12T17:16:43.422467Z",
"active": true,
"title": "Marchew",
"description": null,
"quantity": 10,
"price": "2.00",
"category": null
},
"quantity": 11,
"total_price": "22.00",
"ordered": true,
"user": 1
},
{
"id": 70,
"product": {
"id": 5,
"created": "2021-11-13T20:03:01.336712Z",
"active": true,
"title": "Makaron",
"description": null,
"quantity": 0,
"price": "0.00",
"category": null
},
"quantity": 1,
"total_price": "0.00",
"ordered": true,
"user": 1
},
{
"id": 71,
"product": {
"id": 6,
"created": "2021-11-13T20:03:12.384605Z",
"active": true,
"title": "Pomidor",
"description": null,
"quantity": 0,
"price": "0.00",
"category": null
},
"quantity": 1,
"total_price": "0.00",
"ordered": true,
"user": 1
}
],
"title": "Zamowienie",
"created": "2021-11-13T19:26:59.545602Z",
"value": "49.92",
"is_paid": false,
"ordered": true,
"user": 1
}
I'm just getting started with using normalizr with Redux, and I can't make it work. Even though I can do it with plain JavaScript.
I have an array of objects
const data = [
{
data_detail: [
{
category: 'newCategory',
_id: '123',
},
],
_id: 'abc_id',
customer: {
_id: '456',
email: 'hello#gmail.com',
name: 'Bob',
},
date: '2021-01-10T01:51:24.387Z',
},
];
And I need to transform it to
const normalizedResponse = {
customers: {
'456': {
_id: '456',
email: 'hello#gmail.com',
name: 'Bob',
},
},
details: {
'123': {
category: 'newCategory',
_id: '123',
},
},
orders: {
'abc_id: {
order_detail: [123],
_id: 'abc_id',
customer: '456',
date: '2021-01-10T01:51:24.387Z',
},
},
};
Step 1: Display just orders
What I do:
const userSchema = new schema.Entity(
'orders',
);
const userListSchema = new schema.Array(userSchema);
const normalizedData = normalize(data, userListSchema);
What I get
{
"entities": {
"orders": {
"abc_id": {
"data_detail": [
{
"category": "newCategory",
"id": "123"
}
],
"id": "abc_id",
"customer": {
"id": "456",
"email": "hello#gmail.com",
"name": "Bob"
},
"date": "2021-01-10T01:51:24.387Z"
},
"abc_id-02": {
"data_detail": [
{
"category": "newCategory1",
"id": "123-02"
}
],
"id": "abc_id-02",
"customer": {
"id": "456-02",
"email": "hello#gmail.com",
"name": "Bob"
},
"date": "2001-01-10T01:51:24.387Z"
}
}
},
"result": [
"abc_id",
"abc_id-02"
]
}
What I'm trying to get:
orders: {
'abc_id: {
order_detail: [123],
_id: 'abc_id',
customer: '456',
date: '2021-01-10T01:51:24.387Z',
},
},
The question: How to remove some fields from orders and add new ones?
You have 3 different entity types in your data object. First draft out a schema for each of them:
const detail = new schema.Entity('details');
const customer = new schema.Entity('customers');
const order = new schema.Entity('orders');
Then go back and fill in the relationships. It looks like order is the outermost entity. An order contains an array of details/categories and a single customer.
const order = new schema.Entity('orders', {
data_detail: [detail],
customer,
});
All of your entities use _id instead of id, so you need to set the idAttribute.
Your data is an array of order. You can use new schema.Array(order) but you can also just use [order].
Here's your final code:
const customer = new schema.Entity("customers", {}, { idAttribute: "_id" });
const detail = new schema.Entity("details", {}, { idAttribute: "_id" });
const order = new schema.Entity(
"orders",
{
data_detail: [detail],
customer
},
{ idAttribute: "_id" }
);
const normalizedData = normalize(data, [order]);
That gives you:
{
"entities": {
"details": {
"123": {
"category": "newCategory",
"_id": "123"
}
},
"customers": {
"456": {
"_id": "456",
"email": "hello#gmail.com",
"name": "Bob"
}
},
"orders": {
"abc_id": {
"data_detail": ["123"],
"_id": "abc_id",
"customer": "456",
"date": "2021-01-10T01:51:24.387Z"
}
}
},
"result": ["abc_id"]
}
I'm setting up an action which uses push notifications. Yet, on firebase I can't get "UPDATES_USER_ID" of user to save. It returns "undefined".
I followed the guide on this link and here is my code to get UPDATES_USER_ID.
app.intent('Setup', (conv, params) => {
conv.ask(new UpdatePermission({
intent: "notificationResponseIntent"
}));
});
app.intent("FinishNotificationSetup", (conv, params) => {
if (conv.arguments.get('PERMISSION')) {
conv.data.GoogleUserID = conv.arguments.get("UPDATES_USER_ID");
console.log(conv.data.GoogleUserID);
conv.ask("some response....");
}
});
And here is my webhook request when FinishNotificationSetup intent is invoked.
{
"responseId": "2f425fe5-db42-47dc-90a1-c9bc85f725d2",
"queryResult": {
"queryText": "actions_intent_PERMISSION",
"parameters": {},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
""
]
}
}
],
"outputContexts": [
{
"name": "projects/projectname/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_capability_screen_output"
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_intent_permission",
"parameters": {
"PERMISSION": true,
"text": ""
}
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/_actions_on_google",
"lifespanCount": 98,
"parameters": {
"data": "{\"***":\"***",\"***":\"***"}"
}
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_capability_account_linking"
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_capability_audio_output"
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/google_assistant_input_type_keyboard"
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_capability_web_browser"
},
{
"name": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA/contexts/actions_capability_media_response_audio"
}
],
"intent": {
"name": "projects/projectname-10c22/agent/intents/a12b6d3f-0f24-45e9-a1b2-5649083831b0",
"displayName": "FinishNotificationSetup"
},
"intentDetectionConfidence": 1,
"languageCode": "tr"
},
"originalDetectIntentRequest": {
"source": "google",
"version": "2",
"payload": {
"isInSandbox": true,
"surface": {
"capabilities": [
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
},
{
"name": "actions.capability.ACCOUNT_LINKING"
},
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
}
]
},
"requestType": "SIMULATOR",
"inputs": [
{
"rawInputs": [
{
"inputType": "KEYBOARD"
}
],
"arguments": [
{
"textValue": "true",
"name": "PERMISSION",
"boolValue": true
},
{
"name": "text"
}
],
"intent": "actions.intent.PERMISSION"
}
],
"user": {
"lastSeen": "2019-04-30T07:23:23Z",
"permissions": [
"UPDATE"
],
"locale": "tr-TR",
"userId": "ABwppHHCEdtf23ZaNg0DaCv3fvshSUXUvYGXHe6kR7jbKacwIS6vDBBL7YXbN70jYa8KaXWZqbsyhFFSdsYLiw"
},
"conversation": {
"conversationId": "ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA",
"type": "ACTIVE",
"conversationToken": "[\"_actions_on_google\"]"
},
"availableSurfaces": [
{
"capabilities": [
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.WEB_BROWSER"
}
]
}
]
}
},
"session": "projects/projectname-10c22/agent/sessions/ABwppHGD33Tyho41g9Mr2vzxePlskNmvOzCTxUiDGzENcl3C7RQs94aOQ8ae_DUlOApR0VBO9DuwAWdWr2frAA"
}
To send notification, I've been using userID instead of UPDATES_USER_ID and it is working. Yet, it will be deprecated soon. So, I need to find a solution to get this ID and couldn't make it working. What do I need to do to get this ID?
I've found solution for this problem. While getting UPDATES_USER_ID conv.arguments.get() only works for first attempt. So, while building your action you must save it. If you didn't store or save, you can reset your profile and try again, you will be able to get.
app.intent("FinishNotificationSetup", (conv, params) => {
if (conv.arguments.get('PERMISSION')) {
if(!conv.user.storage.GoogleUserID)
{
conv.user.storage.GoogleUserID = conv.arguments.get("UPDATES_USER_ID");
//For best case
//store ID in your db.
}
console.log(conv.user.storage.GoogleUserID);
conv.ask("some response....");
}
});
For best case, try saving this to your database because conv.user.storage does not work sometimes.
> Locate the `displayBirthdate` function you initially defined, which took no parameter. Modify it to use object de-structuring to get just the 'dob' property of the parameter object it will receive
here's the code
```javascript
const displayBirthdate = () => {};
const displayAddress = () => {};
const displayExtraUserInfo = (extra) => {
document.getElementById("btn-birthdate").addEventListener("click", ()=>
{ displayBirthdate(extra) })
document.getElementById("btn-phone").addEventListener("click", () =>
{ displayPhone(extra) })
document.getElementById("btn-address").addEventListener("click", () =>
{ displayAddress(extra) })
```
adding to the above question
this is the expected response passed as parameter extra
{
"results": [
{
"gender": "female",
"name": {
"title": "ms",
"first": "ceyhan",
"last": "dizdar"
},
"location": {
"street": "3826 şehitler cd",
"city": "tekirdağ",
"state": "bitlis",
"postcode": 11689,
"coordinates": {
"latitude": "79.1017",
"longitude": "27.1350"
},
"timezone": {
"offset": "+5:45",
"description": "Kathmandu"
}
},
"email": "ceyhan.dizdar#example.com",
"login": {
"uuid": "34eb65b2-0535-4656-bd68-4da69dc6d016",
"username": "orangefish864",
"password": "grandpa",
"salt": "vowzvAS2",
"md5": "cf4a7f3210ef97e8e72defafd80b94c8",
"sha1": "4f2af3439862b9bf25757ee73df8cd410ce201a2",
"sha256":
"1497acbca446b5fa47d4bc5ffe4e82c17818176596b66d94f213f091c8ed8077"
},
"dob": {
"date": "1979-08-10T22:03:55Z",
"age": 39
},
"registered": {
"date": "2008-05-24T13:30:20Z",
"age": 10
},
"phone": "(873)-801-4132",
"cell": "(751)-606-5317",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/59.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/59.jpg",
"thumbnail":
"https://randomuser.me/api/portraits/thumb/women/59.jpg"
},
"nat": "TR"
}
],
"info": {
"seed": "008a9fe3a638239b",
"results": 1,
"page": 1,
"version": "1.2"
}
}
Now the question is this:
write an arrow function example displayBirthdate(), pass in this object(extra) as a parameter. using de-structuring method, grab the "dob" property in the object(extra).
below is how i have attempted to solve the question:
const displayBirthdate = (obj) =>{
const{results} = obj;
const[{dob}] = results;
}
but it appears to be incorrect. please any help would be appreciated. thank you
const displayBirthdate = ({dob}) => {};
const displayAddress ({location}) =>{};
In your attempted solution, you were trying to obtain the dob object
from results, but const{results} = obj; evaluates results to
an array. const {dob} = results[0]; in my answer gets the dob object from the first element in the results array. Cheers!
const displayBirthdate = (obj) =>{
const{results} = obj;
const[{dob}] = results;
}
const displayBirthdate = (obj) =>{
const {results} = obj;
console.log(results); // this is an array with the required object at index = 0
const {dob} = results[0];
console.log(dob); // dob is the object you are looking for
}
I have a resource (e.g. posts) which has a to-many relationship to other resources (e.g. comments). I do not need any fields of the related resource but their self-links (to fetch them asynchronously on demand). The response should look something like this:
{
"links": {
"self": "http://example.com/posts/1?xxx",
},
"data": [{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"comments": {
"links": {
"self": "http://example.com/posts/1/relationships/comments",
"related": "http://example.com/posts/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
},
"links": {
"self": "http://example.com/posts/1"
}
}],
"included": [{
"type": "comments",
"id": "5",
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"links": {
"self": "http://example.com/comments/12"
}
}]
}
My question is how should the URL for the request look like?
My idea would be to include the comments and then use an empty sparse fieldset to avoid getting any comment fields but just the self link (+id, type).
Hence, it should look something like http://example.com/posts/1?include=comments&fields[comments]=[].
So I need something like an empty sparse fieldset.
The JSON API specification does not say much about sparse fieldsets (http://jsonapi.org/format/#fetching-sparse-fieldsets) and their relation to links.
The JSON API was able to answer my question.
In short, the correct way to specify an empty sparse fieldset would be:
http://example.com/posts/1?include=comments&fields[comments]=
There's a discussion going on about whether to include the links to individual relationship items in the relationship object:
{
"links": {
"self": "http://example.com/posts/1",
},
"data": [{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"comments": {
"links": {
"self": "http://example.com/posts/1/relationships/comments",
"related": "http://example.com/posts/1/comments",
"item": [ "http://example.com/comments/5", "http://example.com/comments/12" ]
},
"data": [{
"type": "comments",
"id": "5",
"links": {
"about": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"links": {
"about": "http://example.com/comments/12"
}
}]
}
},
"links": {
"self": "http://example.com/posts/1"
}
}]
}