Displaying response from API in react component - javascript

I'm trying to display the response from the API into my react component but it's not working. If I try to use it in the console, I can see the data and its value but not in the react component, it's empty when I try to show the value in a div.
Here is the code where I'm trying to display it in my react component:
const CharacterListing = () => {
const characters = useSelector(getAllCharacters);
console.log("Hello", characters);
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
<div>{value.name}</div>
})
return (
<div>
{renderCharacters}
</div>
);
};
export default CharacterListing;
This is the code for my Character Slice Component
const initialState = {
characters: {},
};
const characterSlice = createSlice({
name: 'characters',
initialState,
reducers: {
addCharacters: (state, { payload }) => {
state.characters = payload;
},
},
});
export const { addCharacters } = characterSlice.actions;
export const getAllCharacters = (state) => state.characters.characters;
export default characterSlice.reducer;
This is the code for my Home Component:
const Home = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchCharacters = async () => {
const response = await baseURL.get(`/characters`)
.catch(error => {
console.log("Error", error);
});
dispatch(addCharacters(response.data));
console.log("Success", response);
};
fetchCharacters();
}, [])
return (
<div>
Home
<CharacterListing />
</div>
);
};
export default Home;
Thank you

You forgot to return item into your map func
Try this :
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
return <div key={key}>{value.name}</div>
})

Related

How can I make the data inside of my Cart become persistent?

I have a NextJS application that is using the ShopifyBuy SDK. I have been successfully able to implement a solution where I am able to fetch the products from Store and display them to the User. The user is also able to go to a product page and add the product to the cart.
However, when the user refreshes the page, the cart is reset, and the data does not persist. The code is below:
context/cart.js:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
import Cookies from "js-cookie";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState = {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const savedState = Cookies.get("cartState");
const [state, dispatch] = useReducer(reducer, savedState || initalState);
useEffect(() => {
Cookies.set("cartState", state, { expires: 7 });
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>
{children}
</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);
products/[handle].tsx:
import React, { useState, useEffect } from "react";
import client from "../../lib/client";
import { useCartDispatch, useCartState } from "../../context/cart";
import Link from "next/link";
import cookie from "js-cookie";
export const getStaticPaths = async () => {
const res = await client.product.fetchAll();
const paths = res.map((product: any) => {
return {
params: { handle: product.handle.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context: any) => {
const handle = context.params.handle;
const res = await client.product.fetchByHandle(handle);
const product = JSON.stringify(res);
return {
props: {
product,
},
};
};
function Product({ product }: any) {
const { state } = useCartState();
const { setCart } = useCartDispatch();
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
setCart(res);
};
product = JSON.parse(product);
return (
<div>
<div className=" flex-col text-2xl font-bold m-8 flex items-center justify-center ">
<h1>{product.title}</h1>
<button onClick={addToCart}>Add to Cart</button>
<Link href="/cart">Checkout</Link>
</div>
</div>
);
}
export default Product;
pages/cart/index.tsx:
import React, { useEffect } from "react";
import { useCartState, useCartDispatch } from "../../context/cart";
import client from "../../lib/client";
function Cart() {
const { state } = useCartState();
return (
<div>
<h1>Cart</h1>
{state.lineItems &&
state.lineItems.map((item: any) => {
return (
<div key={item.id}>
<h2>{item.title}</h2>
<p>{item.variant.title}</p>
<p>{item.quantity}</p>
</div>
);
})}
</div>
);
}
export default Cart;
I have tried using a library called js-cookie and also localStorage. I'm not sure where the problem lies or if the solutions that I've tried are wrong.
P.S.: I'm fairly new to NextJS and Typescript so go easy on the syntax. This code is for a personal project. Thanks in advance!
Answering this because I ended up coming up with a solution that works for me, at least.
Here it is:
const getCart = async () => {
try {
const checkoutId = Cookies.get("checkoutId");
let cart;
if (checkoutId) {
cart = await client.checkout.fetch(checkoutId);
} else {
cart = await client.checkout.create();
Cookies.set("checkoutId", cart.id);
}
setCart(cart);
} catch (err) {
console.log(err);
}
};
From my understanding, what this does is the following:
Check the cookies to see if one exists called "checkoutId"
If it exists, fetch the cart using that checkoutId
Otherwise, create a new cart and create a cookie using the cart.id that is returned in the response
Then, inside my individual Product page ([handle].tsx), I'm doing the following:
const addToCart = async () => {
const checkoutId = state.id;
const lineItemsToAdd = [
{
variantId: product.variants[0].id,
quantity: 1,
},
];
const res = await client.checkout.addLineItems(checkoutId, lineItemsToAdd);
console.log(res);
if (cookie.get("checkoutId") === undefined) {
cookie.set("checkoutId", res.id);
}
setCart(res);
};
Using cookies to store your object cart, as far as I know, is not a good idea. You could use localStorage, like so:
import { createContext, useContext, useEffect, useReducer } from "react";
import client from "../lib/client";
const CartStateContext = createContext();
const CartDispatchContext = createContext();
const SET_CART = "SET_CART";
const initalState =
typeof localStorage !== "undefined" && localStorage.getItem("cartState")
? JSON.parse(localStorage.getItem("cartState"))
: {
lineItems: [],
totalPrice: 0,
webUrl: "",
id: "",
};
const reducer = (state, action) => {
switch (action.type) {
case SET_CART:
return { ...state, ...action.payload };
default:
throw new Error(`Unknown action: ${action.type}`);
}
};
export const CartProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initalState);
useEffect(() => {
localStorage.set("cartState", JSON.stringify(state));
}, [state]);
useEffect(() => {
getCart();
}, []);
const setCart = (payload) => dispatch({ type: SET_CART, payload });
const getCart = async () => {
try {
const cart = await client.checkout.create();
setCart(cart);
} catch (err) {
console.log(err);
}
};
return (
<CartDispatchContext.Provider value={{ setCart }}>
<CartStateContext.Provider value={{ state }}>{children}</CartStateContext.Provider>
</CartDispatchContext.Provider>
);
};
export const useCartState = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

Array doesn't save information ReactJS

import './App.css';
import io from 'socket.io-client'
import { useEffect, useRef, useState } from 'react'
import React from 'react';
import ReactDOM from "react-dom/client";
const socket = io.connect("http://localhost:3001");
function App() {
const [message, setMessage] = useState("");
const [state, setState] = useState([]);
const [chat, setChat] = useState([]);
const socketRef = useRef();
const sendMessage = () => {
socket.emit("send_message", { message });
};
const renderChat = () => {
return (
chat.map(msg => {
console.log(msg.data)
return (
<h3>{msg.data["message"]}</h3>
)
})
)
}
useEffect(() => {
socketRef.current = io.connect("http://localhost:3001")
socketRef.current.on("receive_message", ({ message }) => {
setChat([ ...chat, { message } ])
})
return () => socketRef.current.disconnect()
},
[ chat ]
)
return (
<div className="App">
<input placeholder="Message..." onChange={(event) => {
setMessage(event.target.value);}}
/>
<button onClick={sendMessage}>Send Message</button>
<h1>Message:</h1>
{renderChat()}
</div>
);
}
export default App;
For some reason the useEffect that needs to store information doesn't work. I have tried a few solutions to store new values in an array useState but I always get this error:
When I do it like this:
useEffect(() => {
socket.on("receive_message", message => {
setChat([...chat, {message}]);
});
}, [socket])
it works but it doesn't save the information (it always has only 1 value which is the latest input text).
You can do it like in the second approach you mentioned, using the previous State:
useEffect(() => {
socket.on("receive_message", message => {
setChat(prevState => [...prevState, {message}]);
});
}, [socket])
You try
useEffect(() => {
socket.on("receive_message", ({ message }) => {
if(!!message){
setChat(prev => [ ...prev, { message } ])
}
})
return () => socket.disconnect()
},[ socket ])

trying to use crawled data but error appears like 'Objects are not valid as a React child (found: [object Promise])'

I want to print out the crawled data from the site I want using Gatsby. But I don't know why this error appears.
here's my crawler
class Crawler {
constructor() {
this.client = axios.create();
}
async crawlNews() {
const url = 'https://finance.naver.com/news/news_list.naver?mode=RANK';
const settedResult = await this.client
.get(url, { responseType: 'arraybuffer' })
.then((response) => {
const setResult = [];
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
$('.simpleNewsList > li').each((i, el) => {
const title = $(el).text();
setResult.push({
id: parseInt(i) + 1,
title: title
.replace(/(\r\n|\n|\r|\t)/gm, '')
.toString(),
});
});
return setResult;
})
.catch((err) => console.error(err));
return settedResult;
}
}
and here's Slide component
import React from 'react';
export function Slide(props) {
const { index, title } = props;
return (
<div>
{index} | {title}
</div>
);
}
here's pages/index.js in gatsby
async function Home() {
const settedResult = new Crawler();
const dataSource = await settedResult.crawlNews();
const result = dataSource.map((obj) => {
<Slide index={obj.id} title={obj.title} />;
});
return <div>{result}</div>;
}
export default Home;
When I run 'gatsby develop' with the above files, an error like the title appears
Maybe you can provide a stackblitz ? Its seems you'r missing a return here :
const result = dataSource.map((obj) => {
<Slide index={obj.id} title={obj.title} />;
});
This should work
const result = dataSource.map((obj) =>
<Slide index={obj.id} title={obj.title} />
);
or
const result = dataSource.map((obj) => {
return <Slide index={obj.id} title={obj.title} />;
};

How do you get an element out of a redux reducer?

I have a react component that has a html button that when clicked calls a function that adds an element to a redux reducer and then redirects to another component. The component that is redirected to needs to set state from the reducer but it won't. I know that it is being added to the array in the reducer because I wrote it as an async await and it redirects after it gets added.
This is the original component
const Posts = () => {
const dispatch = useDispatch();
const getProfile = async (member) => {
await dispatch({ type: 'ADD_MEMBER', response: member })
console.log(member)
window.location.href='/member'
console.log('----------- member------------')
console.log(post)
}
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div>
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
<button onClick={() => getProfile(p.publisher)}>Profile</button>
</tr>})}
</div>
)
}
export default Posts;
This is the 'socialNetworkContract' reducer
import { connect, useDispatch, useSelector } from "react-redux";
let init = {
posts:[],
post:{},
profiles:[],
profile:{},
members:[],
member:{}
}
export const socialNetworkContract = (state = init, action) => {
const { type, response } = action;
switch (type) {
case 'ADD_POST':
return {
...state,
posts: [...state.posts, response]
}
case 'SET_POST':
return {
...state,
post: response
}
case 'ADD_PROFILE':
return {
...state,
profiles: [...state.profiles, response]
}
case 'SET_PROFILE':
return {
...state,
profile: response
}
case 'ADD_MEMBER':
return {
...state,
members: [...state.members, response]
}
case 'SET_MEMBER':
return {
...state,
member: response
}
default: return state
}
};
and this is the component that the html button redirects to
const Member = () => {
const [user, setUser] = useState({})
const [profile, setProfile] = useState({});
const dispatch = useDispatch();
useEffect(async()=>{
try {
const pro = socialNetworkContract.members[0];
setUser(pro)
const p = await incidentsInstance.usersProfile(user, { from: accounts[0] });
const a = await snInstance.getUsersPosts(user, { from: accounts[0] });
console.log(a)
setProfile(p)
} catch (e) {
console.error(e)
}
}, [])
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div class="container">
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
{p.message}
{p.replies}
</tr>})}
</div>
</div>
</div>
</div>
)
}
export default Member;
This is the error I get in the console
Error: invalid address (arg="user", coderType="address", value={})
The functions I'm calling are solidity smart contracts and the have been tested and are working and the element I'm trying to retrieve out of the array is an ethereum address.
incidentsInstance and snInstance are declared in the try statement but I took a lot of the code out to make it easier to understand.
given setUser is async, your user is still an empty object when you make your request.
you could pass pro value instead:
useEffect(async () => {
try {
const pro = socialNetworkContract.members[0];
setUser(pro)
const p = await incidentsInstance.usersProfile(pro, { from: accounts[0] });
const a = await snInstance.getUsersPosts(pro, { from: accounts[0] });
setProfile(p)
} catch (e) {
console.error(e)
}
}, [])
or break your useEffect in two pieces:
useEffect(() => {
setUser(socialNetworkContract.members[0]);
}, [])
useEffect(async () => {
if (!Object.keys(user).length) return;
try {
const p = await incidentsInstance.usersProfile(user, { from: accounts[0] });
const a = await snInstance.getUsersPosts(user, { from: accounts[0] });
console.log(a)
setProfile(p)
} catch (e) {
console.error(e)
}
}, [user])
note: fwiw, at first sight your user state looks redundant since it's derived from a calculated value.

Testing React component with data provided by custom hook

I have created this custom hook to fetch data:
const useSuggestionsApi = () => {
const [data, setData] = useState({ suggestions: [] });
const [url, setUrl] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = () => {
setError(false);
setLoading(true);
if(url) {
fetch(url).then((res) => {
if (res.status !== 200) {
console.error(`It seems there was an problem fetching the result. Status Code: ${res.status}`)
return;
}
res.json().then((fetchedData) => {
setData(fetchedData)
})
}).catch(() => {
setError(true)
})
setLoading(false);
};
}
fetchData();
}, [url]);
return [{ data, loading, error }, setUrl];
}
export default useSuggestionsApi;
It used used in this component to render the response (suggestions).
const SearchSuggestions = ({ query, setQuery}) => {
const [{ data }, doFetch] = useSuggestionsApi();
const { suggestions } = data;
useEffect(() => {
const encodedURI = encodeURI(`http://localhost:3000/search?q=${query}`);
doFetch(encodedURI);
}, [doFetch, query]);
return (
<div className="search-suggestions__container">
<ul className="search-suggestions__list">
{suggestions.map((suggestion) => {
return (
<li className="search-suggestions__list-item" key={uuid()}>
<span>
{suggestion.searchterm}
</span>
</li>
)
})}
</ul>
</div>
);
};
export default SearchSuggestions;
Now I would like to write some unit test for the SearchSuggestions component but I am lost on how to mock the returned data from useSuggestionApi. I tried importing useSuggestionApi as a module and then mocking the response like this but with no success:
describe('SearchSuggestions', () => {
const wrapper = shallow(<SearchSuggestions/>)
it('test if correct amount of list-item elements are rendered', () => {
jest.mock("../hooks/useSuggestionsApi", () => ({
useSuggestionsApi: () => mockResponse
}));
expect(wrapper.find('.search-suggestions__list').children()).toHaveLength(mockResponse.data.suggestions.length);
});
})
I am new to testing React components so very grateful for any input!
This works:
jest.mock('../hooks/useSuggestionsApi', () => {
return jest.fn(() => [{data: mockResponse}, jest.fn()]
)
})
describe('SearchSuggestions', () => {
const wrapper = shallow(<SearchSuggestions query="jas"/>)
it('correct amount of list-items gets rendered according to fetched data', () => {
expect(wrapper.find('.search-suggestions__list').children()).toHaveLength(mockResponse.suggestions.length);
});
})

Categories