So I finally took the deep dive into hooks. Yes, now I get how easy they can be to use. However, I know one of the most important aspects of it is reusable logic. To share the hook between components, and make my functional component(container now?) even cleaner, how would I separate this? I understand that I can create a custom hook, as long as it starts with use. So for instance, I want to fetch a bunch of tickets and get the length, I have the following:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function TicketCounter() {
const [data, setData] = useState([]);
useEffect(() => {
axios.get(`/ticketapi/ticketslist/`)
.then(res => {
if (res.data) {
setData(res.data)
}
})
}
, []);
return (
<React.Fragment>
{data.length}
</React.Fragment>
);
}
export default TicketCounter
What's the best way to do this? What method are you using? Are you storing these hooks inside your src folder? I imagine you have a folder for hooks, with each hook having it's own js file? Anyhoo, thanks in advance folks. I absolutely LOVE react and all it has to offer, and am super excited about hooks (2 months after everyone else lol).
Well I figured it out.
Sep hook file
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const useFetchAPI = (url) => {
const [data, setData] = useState([]);
useEffect(() => {
axios.get(url)
.then(res => {
if (res.data) {
setData(res.data)
}
})
}
, []);
return data;
};
export default useFetchAPI
and then my component(container?)
import React, { useState, useEffect } from 'react';
import useFetchAPI from '../hooks/TicketCounterHook'
function TicketCounter() {
const url = `/ticketapi/ticketslist/`
const data = useFetch(url)
return (
<React.Fragment>
{data.length}
</React.Fragment>
);
}
export default TicketCounter
Related
I am learning Redux and I have deviated from the instructor's code. I am trying to convert my code from context & state into Redux.
Is it advisable to use setReduxObject (setCategoriesMap in my code) and selectReduxObject (selectCategoriesMap in my code) in the same .jsx page? Are there any concerns around this?
Thanks!
My code:
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getCategoriesAndDocuments } from "../../utils/firebase/firebase.utils";
import { setCategoriesMap } from "../../store/categories/categories.action";
import { selectCategoriesMap } from "../../store/categories/categories.selector";
import Category from "../../components/category/category.component";
const Shop = () => {
const dispatch = useDispatch();
useEffect(() => {
const getCategoriesMap = async () => {
const categories = await getCategoriesAndDocuments();
dispatch(setCategoriesMap(categories));
};
getCategoriesMap();
}, []);
const categoriesMap = useSelector(selectCategoriesMap);
return (
<div>
{Object.keys(categoriesMap).map((key) => {
const products = categoriesMap[key];
return <Category key={key} title={key} products={products} />;
})}
</div>
);
};
export default Shop;
This is just the default approach, nothing to be concerned about.
As soon as you're using getCategoriesAndDocuments the same way in another component though, it's better to move this to an async action creator.
Could even do it for this component already to improve separation of concerns. The component does not necessarily need to be involved with firebase, its job is display logic. Wether the data comes from firebase or localStorage or some graphQL server should not matter.
I hava a component called videoRow i try to render this component using dummy values now i get data from a useEffect Hook i have to use that data to render my component but when i try to do so it dont show anything. I even try console log to check weather i get my data or not it print my data on console means my useEffect is working But when i try this data on my videoRow component it not show anything
import React, { useState, useEffect } from "react";
import "../css/searchPage.css";
import TuneSharpIcon from "#mui/icons-material/TuneSharp";
import ChannelRow from "./ChannelRow";
import VideoRow from "./VideoRow";
import { selectInput } from "../features/inputSlice";
import { useSelector } from "react-redux";
import Axios from "axios";
function SearchPage() {
const getQuery = useSelector(selectInput);
const API_URL = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=4&key=APIKEY&type=video&q=${getQuery.input}`;
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
let request = await Axios.get(API_URL);
setData(request);
}
fetchData();
}, [API_URL]);
console.log(data);
return (
<div className="searchPage">
<div className="filter">
<TuneSharpIcon></TuneSharpIcon>
<h2>FILTERS</h2>
</div>
<hr></hr>
<ChannelRow
image="https://images.indianexpress.com/2022/01/Republic-Day_1200_AP2022.jpg"
channelName="Dummy"
verified
subs="670k"
noOfVideos={567}
desc="You can find awesome programming lessons here! Also, expect programming tips and tricks that will take your coding skills to the ..."
></ChannelRow>
<hr></hr>
{data?.data?.items?.forEach((item) => {
console.log(item.snippet.title);
console.log(item?.snippet.thumbnails.high.url)
console.log(item?.snippet.publishedAt)
console.log(item?.snippet.description)
console.log(item?.snippet.channelTitle)
return(<VideoRow
image={item?.snippet.thumbnails.high.url}
channelName={item?.channelTitle}
timestamp={item?.snippet.publishedAt}
title={item?.snippet.title}
desc={item?.snippet.description}
views="1.4M"
subs="1.4M"
></VideoRow>)
})}
</div>
);
}
export default SearchPage;
Change data?.data?.items?.forEach to data?.data?.items?.map. forEach returns nothing. So, even if you return the component from the callback, forEach will just ignore it. But, map will return all transformed results as an array.
You can read more about lists in react here.
Hello I try to save the fatched data from my database to my variable selectedRestaurant. I use setSelectedrestaurant in the useEffekt hook but it doesn't update my variable. I get as a value null.
Here is my code
import React, { useContext, useEffect, useState } from 'react';
import { useParams } from 'react-router';
import RestaurantFinder from '../api/RestaurantFinder';
import { RestaurantsContext } from "../context/RestaurantsContext";
import Reviews from '../components/Reviews';
import StarComponent from '../components/StarComponent';
import AddReview from '../components/AddReview';
import Test from '../components/Test';
const RestaurantDetailedPage = (props) =>{
//const{ selectedRestaurant, setSelectedRestaurant}= createContext(RestaurantsContext);
const[ selectedRestaurant, setSelectedRestaurant]= useState(null);
const {id} = useParams();
useEffect(()=>{
const fetchData = async(id)=>{
const result = await RestaurantFinder.get("/"+id);
console.log(result);
setSelectedRestaurant(result.data.data);
console.log(selectedRestaurant);
}
fetchData(id);
},[]);//Wichtig, damit es nur 1x
/*
useEffect(()=>{
console.log("useEffect2");
console.log(selectedRestaurant);
},[selectedRestaurant]);
*/
return(
<div>{selectedRestaurant && (
<>
<div>{<AddReview/>}</div>
<div></div>
</>
)}
</div>
)
}
export default RestaurantDetailedPage;
I know that useEffect is async so I tried with await setSelectedRestaurant(result.data.data)
but it didn't work. I also defined two useEffects that should invoke only once. One for changing and the other for update but both useEffects are invoked twice. I dont know why and how to solve it.
Hope u can help me
Try tidying up your sample code. There are lot of poorly formatter comments and spelling errors that make it difficult to parse.
EDIT:
When you update the state (i.e. setSelectedRestraunt) those changes are batched together don't change the state variable until the next render loop.
If you want to console.log or otherwise use the data, create a useEffect which is dependent on that value.
import React, { useContext, useEffect, useState } from 'react';
import { useParams } from 'react-router';
import RestaurantFinder from '../api/RestaurantFinder';
import { RestaurantsContext } from "../context/RestaurantsContext";
import Reviews from '../components/Reviews';
import StarComponent from '../components/StarComponent';
import AddReview from '../components/AddReview';
import Test from '../components/Test';
const RestaurantDetailedPage = (props) =>{
const{ selectedRestaurant, setSelectedRestaurant}= createContext(RestaurantsContext);
const {id} = useParams();
useEffect(()=>{
const fetchData = async(id)=>{
const result = await RestaurantFinder.get("/"+id);
console.log(result);
setSelectedRestaurant(result.data.data);
}
fetchData(id);
},[]);
useEffect(()=>{
console.log("Selected Restaurant:", selectedRestaurant);
},[selectedRestaurant]);
return(
<div>{selectedRestaurant && (
<>
<div>{<AddReview/>}</div>
<div></div>
</>
)}
</div>
)
}
export default RestaurantDetailedPage;
Original comment
With that in mind, your <Reviews> element is commented out, is that intentional?
{/* <Reviews reviewsObject={selectedRestaurant.reviews}/>*/}
I am trying to pass a function between two components but even though I do not have any errors, the function that I am passing wont show or to be precise it is not working. I have two files and one of them is creating a context while the other is using it (obviously). Now, they are not shown in App.js (which is rendered in index.js, usual stuff) they are in the seperate folder. I am using React Router to show one the pages (News.js).
Here are the files:
NewsContext.js
import React, { useContext, createContext, useState, useEffect } from "react";
export const newsK = React.createContext();
export const NewsContext = (props) => {
const working = () => {
console.log("it is working");
};
return <newsK.Provider value={working}>{props.children}</newsK.Provider>;
};
export default NewsContext;
News.js
import React, { useContext, useState, useEffect } from "react";
import { newsK } from "./NewsContext";
import { NewsContext } from "./NewsContext";
const News = () => {
const data = useContext(newsK);
return (
<NewsContext>
<div>
<button onClick={data}></button>
</div>
</NewsContext>
);
};
export default News;
When i pressed the button, it wont do anything. Any tips?
You're trying to use context outside the NewsContext component
The solution for this will be to create a component that will call useContext inside NewsContext.
I.e.
const WrappedButton = () => {
const data = useContext(newsK)
return <button onClick={data} />
}
And then put it inside the NewsContext:
<NewsContext>
<div>
<WrappedButton />
</div>
</NewsContext>
I have react component look like this following code:
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link, useParams } from "react-router-dom";
import { createClient, getClients } from "../redux/actions/clients";
function UpdateClient(props) {
let params = useParams();
const { error, successSubmit, clients } = useSelector(
(state) => state.clients
);
const [client, setClient] = useState(clients[0]);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getClients({ id: params.id }));
}, []);
const submitClient = () => {
dispatch(createClient(client));
};
return (
<div>{client.name} {clients[0].name}</div>
);
}
export default UpdateClient;
And the result is different client.name return test1,
while clients[0].name return correct data based on route parameter id (in this example parameter id value is 7) which is test7
I need the local state for temporary saving form data. I don't know .. why it's become different?
Can you please help me guys? Thanks in advance
You are referencing a stale state which is a copy of the clients state.
If you want to see an updated state you should use useEffect for that.
useEffect(() => {
setClient(clients[0]);
}, [clients]);
Notice that duplicating state is not recommended.
There should be a single “source of truth” for any data that changes in a React application.