I have a custom react hook written that is supposed to fetch a list of categories from a firebase firestore. I then map over this return array to display a list of the categories to the user.
However when I call the custom hook it is not creating the list on the front-end. When I console.log the array it first logs null then logs the correct list. I have a feeling that the react component that display the list is trying to display the first value that gets returned which is null.
Below is my custom hook and the component that returns to list:
import { useState, useEffect } from 'react';
import firebase from '../firebase/firebase';
function useCategories() {
const [categories, setCategories] = useState(null);
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection('categories')
.onSnapshot((snapshot) => {
const fetchedCategories = snapshot.docs.map((document) => (
document.data().name
))
setCategories(fetchedCategories)
})
return () => unsubscribe() // drop subscription to firestore once components unmounts
}, []);
return categories
}
export default useCategories;
import React from 'react';
import useCategories from '../helpers/useCategories';
const CategoriesList = () => {
const categories = useCategories();
return (
<ul>
{categories && categories.map(category => (
<li key={category.name}>
{category.name}
</li>
))}
</ul>
);
}
export default CategoriesList;
I feel like this is probably an easy fix but I have brain mush
Cheers
Related
What I am trying to do is to add components iteratively from the last dynamically added component.
|dynamically added component| ----on click----> |added new component| ---on click--> | added another component|
import React, { useState } from 'react';
import Product from '../../Product';
import Project from '../../Project';
const NewProject = () => {
const [productList, setProductList] = useState([]);
const addProduct = () => {
setProductList([...productList, <Product addProduct={addProduct} key={productList.length}></Product>]);
};
return <React.Fragment>
<Project addProduct={addProduct}>
</Project>
{productList}
</React.Fragment>
}
export default NewProject;
Basically, I'm passing addProduct const to the Product component and it executes the addProduct function again. I see addProduct is triggered but I don't see component came. I think I'm mixing some states there.
Adding Product component from Project component props works fine by the way. But I want to do the same with added product components.
I don't know the exact problem but the way I use it is a little bit different instead of saving Product Component to the list, I only save the product data then I create a another function to render them into components and then pass it in return like below:
import React, { useState } from 'react';
import Product from '../../Product';
import Project from '../../Project';
const NewProject = () => {
const [products, setProducts] = useState([{}]);
const addProduct = () => {
setProducts([...products, {}]);
};
const renderProducts = () => products.map((_, i) => <Product addProduct={addProduct}/>)
return <>{renderProducts()}</>;
};
export default NewProject;
I am trying to map over an employee list after useEffect fetches the array and I dispatch it to my redux-state. If I console log it I see an array full of objects, but my component doesn't appear to be re-rendering. Any suggestions?
import React, { useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setEmployees } from '../redux/reducers/employeeListReducer'
import employeesService from '../back-end/services/employees'
const EmployeeList = ({ }) => {
const dispatch = useDispatch()
const employees = useSelector(state => state.employeeList)
const user = useSelector(state => state.user)
employeesService.setToken(user.token)
useEffect(() => {
(async () => {
const employeeList = await employeesService.getAll()
dispatch(setEmployees(employeeList))
})();
}, [dispatch])
if (employees === null) {
return <p>Loading Employees...</p>
}
return (
<div>
{console.log(employees)}
{employees.map(e =>
<div key={e.id}>
{e.name} {e.phone} {e.email}
</div>
)}
</div>
)
}
export default EmployeeList
It is unclear from the description what may be causing the first issue you describe (there is no console.log we can look at to determine why there is a discrepancy), but you cannot call hooks from within callbacks provided to other hooks. This is a limitation of the how hooks are implemented. They depend on always being called in the same order (so you cannot conditionally call a hook function), and they must all have been called by the time your function returns. Details can be found here:
https://reactjs.org/warnings/invalid-hook-call-warning.html
Basically, you must call hook functions within the top level of a functional component, or within the top level of another hook (for instance, you will see hooks that are implemented using other hooks). All hook functions must have been called by the time your function returns. If the hook is called within a callback, it very likely will be called after your function returns. So React warns you about this.
All of that said, moving your declaration of dispatch to the top level of your component should resolve this issue:
import React, {useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setEmployees} from '../redux/reducers/employeeListReducer'
import employeesService from '../back-end/services/employees'
const EmployeeList = ({ employee }) => {
const dispatch = useDispatch()
const employees = useSelector(state => state.employeeList)
const user = useSelector(state => state.user)
employeesService.setToken(user.token)
useEffect(() => {
employeesService
.getAll()
.then(employeeList => {
dispatch(setEmployees(employeeList))
})
}, [dispatch])
return (
<div>
Employee List Placeholder
{employees.map(e =>
<div key={e.id}>
{e.name} {e.phone} {e.email}
</div>
)}
</div>
)
}
export default EmployeeList
I have a need to fetch data from an API on component load, am using axios to fetch data, I need to save the response to the state and get back when the component load.
But i could do as am new to this.
My codes as below.
Sales.js : (This is where I fetch My components)
function SalesDesk() {
return (
<div>
<FoodScreen />
</div>
)}
export default SalesDesk;
FoodScreen.js (This is where i need to list my results to a variable, to map it later)
function FoodScreen() {
return(
<div className="sdFoodScreenMain">
{console.log(items)} // The results should be displayed here
</div>
)}
export default FoodScreen;
API.js (Here is where where i use my axios Router)
const API_URL = `https://jsonplaceholder.typicode.com/`; //Mock api for test purposes
export const GetAllItems = () => {
return (dispatch) => {
axios.get(API_URL)
.then(response => {
dispatch(allItemsList(response.data));
})
}
};
ItemsReducer.js (The reducer Logic)
const ItemsReducer =(state:Array = null, action) =>{
if (action.type === 'ALL_ITEMS') {
return GetAllItems ;
} else {
return state= null;
}
};
export default ItemsReducer
SalesAction.js (Action list)
export const allItemsList = () => {
return {
type: 'ALL_ITEMS'
};
};
All I need to do is fetch the the data from the API and display it in the console, when the component renders.so that I can display it in a map of div boxes for future purposes. Am new to both react and Redux, so ignore if any logic or implementation issues.
At first Router.js is a bad name(api.js etc), You should connect Sales.js to redux, using { connect } from 'react-redux'. See there https://redux.js.org/basics/usage-with-react and call action to fetch data in Sales.js
All I had to add an useDispatch on the component render, so it could fetch the data to the component on load.
import Reactfrom 'react'
import {useDispatch} from "react-redux";
import {GetAllItems} from 'API' //File containing the axios function
export function SalesDesk() {
const dispatch = useDispatch();
dispatch(GetAllItems())
return (
<div>
<FoodScreen />
</div>
)}
This helped me to fetch add add to state on component load.
I'm having some troubles in a implementation with React, Redux and Hooks.
I do not know how to avoid this:
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import * as R from "ramda";
function Main() {
const mainBanners = useSelector(state => state.mainBanners);
const features = useSelector(state => state.features);
const banners = useSelector(state => state.banners);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchMainBanners());
dispatch(fetchFeatures());
dispatch(fetchBanner());
}, [dispatch]);
console.log(features);
return (
<div className="Main">
{R.isEmpty(mainBanners || features) ? (
<p>Loading...</p>
) : (
<MainBanner mainBanners={mainBanners} features={features} />
)}
<Banners banners={banners} />
</div>
);
}
export default Main;
The result console.log of this example is as follows:
You have three different useSelector calls, and you are executing fetches for three different sets of data. So yes, I would expect that to result in a total of four separate renders:
Initial render
Render after mainBanners is fetched
Render after features is fetched
Render after banners is fetched
This is both expected based on the code you have written, and fine in general, given that your component might want to render something different when any of those changes.
I have read about React hooks and wanted to try it on this new website I have been developing. I am trying to pass an async function as a prop. Part of the function is to set my "cities" state and console log just to see if the API works. I have been receiving "setCities is not a function" error or, cities returns undefined if I just console log it directly. I think the function can't access the {cities, setCities} state. I have tried it in class component and it works. Has anyone encountered similar situations? Below is the code sample:
import React, { useState } from "react";
import axios from "axios";
import Menu from "./Menu";
function App() {
const { cities, setCities } = useState([]);
const searchSubmit = async term => {
try {
const res = await axios.get("http://localhost:8080/general/search", {
params: { city: term }
});
setCities(res.data);
console.log(cities);
} catch (err) {
console.log(err.message);
}
};
return (
<div className="ui container">
<Menu handleSearchSubmit={searchSubmit} />
</div>
);
}
useStates return an array, not an object...
So you can change
const { cities, setCities } = useState([]);
to
const [ cities, setCities ] = useState([]);
And it should work.