React increment likes to an API endpoint - reactjs

Im trying to make a system of likes for each recipe that i have on my page. The endpoint works, i basically pass the id and when i submit, it increments the number of likes by one of that recipe.
Im trying to make the frontend part for it.
Basically im building a custom hook with
import { useState, useEffect } from "react";
import axios from "axios";
const useLikes = (id) => {
const [like, setLike] = useState();
useEffect(() => {
(async (id) => {
const response = await axios.post(`
https://obscure-river-28733.herokuapp.com/recipe/like/${id}
`);
setLike(response.data);
})(like);
});
console.log("like" + like);
return like;
};
export default useLikes;
Now this is the part im a bit stuck on what to do.
Im bulding the icon like this
import React from "react";
import { BsHeart } from "react-icons/bs";
import useLikes from "./useLikes";
const Likes = ({ id }) => {
const likes = useLikes(id);
return (
<BsHeart
likes={id}
color="red"
size={18}
onClick={() => console.log(likes)}
/>
);
};
export default Likes;
And on the app component inside my map i call it like this
<Likes likes={record.id} />
id comes undifined so its not making the post to nothing viable. Could anyone help me solve this please?

Your Likes component should be defined like this:
const Likes = ({ likes: id }) => {
or change the prop name when calling it
<Likes id={record.id}/>
Api call is asynchronous so it should be done simply inside an asynchronous function
const useLikes = async (id) => {
const response = await axios.post(`
https://obscure-river-28733.herokuapp.com/recipe/like/${id}
`);
return response.data;
};
Then in the Likes component you can use the useLikes function like this
const [likes, setLikes] = useState(0);
useLikes(id).then((res) => setLikes(res));

Related

Current user with React Context

I want to have a way to get and fetch the current user using React Context (to not pass props to all my components)
I tried using React Context but didn't understand how I would achieve something like const { currentUser, fetchCurrentUser } = useCurrentUser() from the docs.
here is what i did for my project:
// src/CurrentUserContext.js
import React from "react"
export const CurrentUserContext = React.createContext()
export const CurrentUserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = React.useState(null)
const fetchCurrentUser = async () => {
let response = await fetch("/api/users/current")
response = await response.json()
setCurrentUser(response)
}
return (
<CurrentUserContext.Provider value={{ currentUser, fetchCurrentUser }}>
{children}
</CurrentUserContext.Provider>
)
}
export const useCurrentUser = () => React.useContext(CurrentUserContext)
and then use it like this:
setting up the provider:
// ...
import { CurrentUserProvider } from "./CurrentUserContext"
// ...
const App = () => (
<CurrentUserProvider>
...
</CurrentUserProvider>
)
export default App
and using the context in components:
...
import { useCurrentUser } from "./CurrentUserContext"
const Header = () => {
const { currentUser, fetchCurrentUser } = useCurrentUser()
React.useEffect(() => fetchCurrentUser(), [])
const logout = async (e) => {
e.preventDefault()
let response = await fetchWithCsrf("/api/session", { method: "DELETE" })
fetchCurrentUser()
}
// ...
}
...
the full source code is available on github: https://github.com/dorianmarie/emojeet
and the project can be tried live at: http://emojeet.com/
If useContext returns undefined, then you might have forgotten the Provider or need to move your provider up the stack.
U dont explained what u want to do with the data but...After u exec the function fetch in use effect.
Now u have the object user in the state currentUser.
Try to console log after the use effect the currentUser and see what dat inside it.
After u can use it with currentUser."whatever prop inside"..

Beginner with Conversion of code(from function to class) in react js

import React, { useEffect, useState } from "react";
import { toast } from "react-toastify";
const Dashboard = ({ setAuth }) => {
const [name, setName] = useState("");
const [role, setRole] = useState("");
const getProfile = async () => {
try {
const res = await fetch("http://localhost:5000/dashboard/", {
method: "POST",
headers: { jwt_token: localStorage.token }
});
const parseData = await res.json();
console.log(parseData)
setRole(parseData.user_role);
setName(parseData.user_name);
} catch (err) {
console.error(err.message);
}
};
const logout = async e => {
e.preventDefault();
try {
localStorage.removeItem("token");
setAuth(false);
toast.success("Logout successfully");
} catch (err) {
console.error(err.message);
}
useEffect(() => {
getProfile();
}, []);
return (
<div>
<h1 className="mt-5">Dashboard</h1>
<h2>Welcome {name} as {role}</h2>
<button onClick={e => logout(e)} className="btn btn-primary">
Logout
</button>
</div>
);
};
export default Dashboard;
Hi all,
Please help...
I am trying to develop an app with a simple login registration .
I have a code in function component for login registration which I have posted above .
My entire codes of the app are in class based(class components). Could you please help me to convert the above code into
class based.
You can convert your function component to a class in five steps:
Create an ES6 class, with the same name, that extends React.Component.
Add a class constructor that assigns the initial this.state (to add your initial state of name and role ).
use this.setState to update your state ( name or role).
Add a single empty method to it called render().
Move the body of "return" inside the function component into the render() method.
Replace props with this.props in the render() body.
you can't use react Hooks (useEffect, useState ) inside you class component , so you will need to use ComponentDidMount or ComponentDidUpdate ...(depending in the situation ), in your case you will need to use ComponentDidMount because you fetching data (call getProfile inside ComponentDidMount).
you need to take a look in the references below to understand more about it and why you will need to use componentDidMount:
https://reactjs.org/docs/state-and-lifecycle.html#converting-a-function-to-a-class
https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/

Data fetched with axios flickering on page load

I'm building out a new marketing website for my company in Gatsby, utilizing axios to fetch data from a REST api to dynamically display a list of dealers, and then dynamically displaying the contact information of the chosen dealer throughout the site. This is done by setting a cookie, which carries the ID of the dealer on the API, and then will fetch that dealer's info based on the cookie. However, I'm encountering an issue where the name of the dealer, which I'm currently displaying in the header, flickers on every page load. It looks bad, so I'm wondering if there is a way to either cache that data, or not force it to fetch on every page load and eliminate the flicker. I'm still in development, so I've got it staged on Netlify here, and you can take a look at the live version.
Here is my hook.
use-fetch.ts
import { useEffect, useState } from 'react';
import axios from 'axios';
export const useFetch = (url: string) => {
const [status, setStatus] = useState('idle');
const [data, setData] = useState([]);
useEffect(() => {
if (!url) return;
const fetchData = async () => {
setStatus('fetching');
const result = await axios(url);
setData(result.data);
setStatus('fetched');
};
fetchData();
}, [url]);
return { status, data };
};
I'm then able to consume this in the pages like so:
const [query] = useState('https://jsonplaceholder.typicode.com/users/1/');
const url = query && 'https://jsonplaceholder.typicode.com/users/${cookie}';
const { data } = useFetch(url);
This sets an initial state users/1/ that will display the information for the first dealer unless a cookie is set.
I use this in a layout component, and I can pass the data prop down to my Header component.
app-layout.tsx
import React, { ReactNode, useEffect, useState } from 'react';
import Logo from '../../assets/svg/logo.svg';
import { Header } from '../header/Header';
import { Footer } from '../footer/Footer';
import { Devtools } from '../devtools/Devtools';
import s from './AppLayout.scss';
import { useCookie } from 'hooks/use-cookie';
import { useFetch } from 'hooks/use-fetch';
interface AppLayoutProps {
menuItems: any;
children: ReactNode;
}
const isDev = process.env.NODE_ENV === 'development';
// tslint:disable no-default-export
export default ({ children, menuItems }: AppLayoutProps) => {
// copyright year
const [year, setDate] = useState<any>();
// setting cookie to be referenced in the useFetch hook, setting the query for dealer specific information
const [cookie] = useCookie('one-day-location', '1');
// the API call
const [query, setQuery] = useState('https://jsonplaceholder.typicode.com/users/1/');
const url = query && `https://jsonplaceholder.typicode.com/users/${cookie}`;
const { data } = useFetch(url);
const getYear = () => setDate(new Date().getFullYear());
useEffect(() => {
getYear();
}, []);
return (
<div className={s.layout}>
<Header menuItems={menuItems} data={data}></Header>
{children}
<Footer menuItems={menuItems} logo={<Logo />} year={year} />
{isDev && <Devtools />}
</div>
);
};
And this is my use-cookie hook that is referenced throughout these components:
use-cookie.ts
import { useState } from 'react';
import Cookies from 'universal-cookie';
/**
* Custom hook creates and returns cookie values.
*/
export const useCookie = (key: string, value: string) => {
const cookies = new Cookies();
const [cookie] = useState(() => {
if (cookies.get(key)) {
return cookies.get(key);
}
cookies.set(key, value);
});
const updateCookie = (value: string) => {
removeItem(value);
cookies.set(key, value);
};
const removeItem = (key: string) => {
cookies.remove(key);
};
return [cookie, updateCookie, removeItem];
};
If you notice though, it flickers on every page load. Is there a way to store and display that data differently so that it won't do this?
Thanks in advance.
So, I was able to figure out a better solution with a bit of digging. Rather than trying to debug the hook that I built, I'm using axios-hooks, which has all of the same functionality that I needed, but solves the problem. In my layout, I've got the data being fetched like so:
const [cookie, updateCookie] = useCookie('one-day-location', '1');
const [{ data, loading, error }] = useAxios(
`https://jsonplaceholder.typicode.com/users/${cookie}`,
);
And I'm then able to pass the data down to my Header component, but it doesn't load the API multiple times, and eliminates the flickering. I can absolutely add more documentation here in my answer if anyone has any more questions.

this axios api call returns an empty array for the call, but the second one returns a value

I am trying to use the context below to fetch the data from the api, then pass it down to other components. The data is from unsplash, so I would like to filter it out first using a keyword set by the user. However, the first array returned by the function is empty, but if I console.log() it, I get all the data as expected
import React, {useState, createContext} from 'react';
import axios from 'axios';
export const ImageContext = createContext();
export const ImageContextProvider = ({children}) => {
const [images, setImages] = useState([]);
const count = 15;
const start = 1;
const getImages = async (keyword) => {
await axios.get(`api/photos?count=${count}&start=${start}&keyword=${keyword}`)
.then(res => setImages(res.data.results));
}
return (
<ImageContext.Provider value={{images, getImages: getImages }}>
{children}
</ImageContext.Provider>
);
}
This is the component that passes the keyword to the context that will be used to fetch data from the API
import React, { useState, useContext} from 'react';
import { ImageContext } from '../../Context/ImageContext';
export const SearchBar = () => {
const { getImages, images } = useContext(ImageContext);
const[keyword, setKeyword] = useState('');
const searchImages = async (e) => {
e.preventDefault();
await getImages(keyword);
console.log(images);
}
return (
<form onSubmit={searchImages}>
<input type='text' placeholder='Search Images' value={keyword} onChange={(e) =>
setKeyword(e.target.value)}/>
<input type='submit'/>
</form>
);
}
You are almost there :)
Since you are using React hooks, you need useEffect so that you can be able to send Http request. It is equivalent to `componentDidMount.
How to use it?
import React, {useState, createContext, useEffect} from 'react';
useEffect(() => {
const result = axios.get(`api/photos?count=${count}&start=${start}&keyword=${keyword}`)
.then(res => setImages(res.data.results)); // remember to update your state
}, []); // don't forget to pass an empty array
Take a look at your getImages() function:
const getImages = async (keyword) => {
await axios.get(`api/photos?...`).then(...);
}
If I’m right that is a wrong construction as you’re mixing and await and a then(). Furthermore, your getImages() is not returning a value. I think the following solves your problem:
const getImages = async (keyword) => {
const res = await axios.get(`api/photos?...`);
setImages(res.data.results);
return res.data.results;
}

React JS: Get Context Data After getting success on API Call

I am stuck at getting context data.
I have a context and a component which uses its data.
I need to get the updated data of context's variable on API call success in my component.
so How can I do that ?
Here what I have tried.
context.js
import React, { useState, createContext,useEffect } from 'react';
import {getData} from './actionMethods';
const NewContext = createContext();
function newContextProvider(props) {
const [dataValue, setData] = useState([])
useEffect(() => {
const fetchMyData = async () => {
const dataValue = await getData(); // this is an API call
setData(dataValue)
};
fetchMyData();
}, []);
return (
<NewContext.Provider
value={{
state: {
dataValue
},
actions: {
}
}}
>
{props.children}
</NewContext.Provider>
);
}
const newContextConsumer = newContext.Consumer;
export { newContextProvider, newContextConsumer, newGridContext };
myComponent.js
import React, { useState, useContext } from 'react'
import context from './context'
import deleteAPI from './actionMethods'
function myComponent(props) {
const id= 10
const {state,actions} = useContext(context)
deleteAPI(id).then(res => {
if (res){
// what should I write here to get the updated Data from the context which will call an API to get the updated data.
}
})
}
Any help would be great.
Thank You.
As a generic example, one option is to fetch the data from the server when the app loads in the front-end. From there you can send requests to modify the server data and at the same time update your local version. Something like:
Fetch data and save it to the local store: [{id: 0, name: 'first'},{id: 1, name: 'second'}]
Modify the data sending a request to the server. For example deleting an item. id: 0
Once the server responds confirming the operation was successful you can modify that data in the local store. [{id: 1, name: 'second'}]
You can handle the data using a Redux store or a React Context. For example, using a Context:
export const ItemsContext = createContext([]);
export const ItemsContextProvider = props => {
const [items, setItems] = useState([]);
const deleteItem = id => {
deleteItemsAxios(id).then(() => {
setItems(items => items.filter(item => item.id !== id));
});
};
useEffect(() => {
const fetchItems = async () => {
const items_fetched = await fetchItemsAxios();
if (items_fetched) {
setItems(items_fetched);
} else {
// Something went wrong
}
};
fetchItems();
}, []);
return (
<ItemsContext.Provider
value={{
items,
deleteItem
}}
>
{props.children}
</ItemsContext.Provider>
);
};
We define a Component that will manage the data fetch. The data items are inside a state. When the Component mounts we fetch the items and save them in the state. If we want to delete an item we first call the corresponding fetch function. Once it finishes, if it was successful, we update the state and remove that item. We use React Context to pass the items data, as well as the deleteItem function, to any component that needs them.
Let me know if you need more explanation.

Resources