I'm trying to load a component using useEffect hook and axios. However, when loading the page the components are not render, but when inspecting the parent component, his children are undefined (I'm very new to react)
Component
export const BonusRows = () => {
const [state, setState] = useState([]);
const [order, setOrder] = useState("asc");
const bonusRequest = () => {
axios
.get(`http://localhost:4000/api/v1/process/creator/gutierad5`)
.then((res) => {
const bonus = res.data;
const processes = bonus.processes;
setState(processes);
console.log(processes);
});
};
useEffect(() => {
bonusRequest();
}, []);
return (
<ProcessTable
funcionality={() => {
sortByDate(state, setState, order, setOrder);
}}
>
<Accordion allowToggle allowMultiple>
{state.map((element, index) => {
<AccordionItemSlot
key={index}
proccessID={element.id}
title={element.name}
targeruser='gutierad5'
createDate={FormatDateInYYMMDD(element.createdAt)}
status={element.status}
creator={element.creator}
links={element.links}
amount={element.processBonus.amount}
updatedAt={element.updatedAt}
password={element.processBonus.password}
franchise={element.processBonus.franchise}
/>;
})}
</Accordion>
</ProcessTable>
);
};
I don't know what I'm doing wrong. My best guess is that the state is not present when the component is loaded, so thet's the reasong is undefined. However when console log the state, the data is there.
Related
Following code to fetch data from firebase firestore and set to a state 'products' in useEffect :
const { firebase } = useContext(FirebaseContext)
const [products, setProducts] = useState([])
const db=firebase.firestore();
useEffect(() => {
const unsubscribe = db.collection("products").onSnapshot((snapshot) => {
const newProducts = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setProducts(newProducts);
console.log( products);
});
return () => unsubscribe();
}, []);
when first render products is consoling empty array.(data are correctly fetching from firestore and coming to 'newProducts' as array of objects).
during running, if something is changed in the code, server restarts, and now products state is consoling correctly..
and when refreshing browser, products are again consoling empty array..
I am expecting 'products' state is setting the values from firestore in the first working of useEffect
I came to this state updation on rendering my component as follows..
return
<div>
<h4>{product.name}</h4>
<p> {product.price}</p>
<p>{product.type}</p>
</div>
})
}
All these are not running since products state is empty on initial render...
Hint: its because of asynchronous nature of setproduct().
How can i make the code in such a way that, page is loaded only after setting the state..
How can i solve this issue..?
it's because component is not rerendered yet and Products state is still showing previous value,
if you want to see it's value use it outside of useffect or log prevState in setProduct callback to see it's value
like this :
useEffect(() => {
const unsubscribe = db.collection('products').onSnapshot(snapshot => {
const newProducts = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}));
setProducts(newProducts);
setProducts((prevState)=>{
console.log(prevState)
return prevState
});
});
return () => unsubscribe();
}, []);
or you can use another useEffect to see it's new value, like this:
useEffect(()=>{
console.log(products);
},[products])
Move the console.log(products) to right after declaring your product's useState hook.
State updates in react (which is setState) can happen asynchronously. Therefore, there is no guarantee the next line has the updated state. What you really care about is the state when the component renders
You can conditionally render the component based on any state variable let's say productLoading.
You can use the useState hook and a state variable productLoading to solve this issue.
You can set the productLoading to true at the beginning of the component and set it to false after the state products has been set.
Here's an example of how you could implement this:
const { firebase } = useContext(FirebaseContext);
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const db = firebase.firestore();
useEffect(() => {
const unsubscribe = db.collection("products").onSnapshot((snapshot) => {
const newProducts = snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setProducts(newProducts);
setLoading(false);
});
return () => unsubscribe();
}, []);
return (
<div>
{loading ? (
<div>Loading...</div>
) : (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
)}
</div>
);
This will enable to render the component only after products data is available
I have a callback method that calls a forceUpdate() hook in parent. I expect this to re render and call ChildA with updated props. These values are updated in another component say ChildB.
While I keep a debugger at callback method, I see updated values for props and Im getting a hit to return method as well. But the child component is not getting hit at all.
const Body = FC =>{
const [state, setState] = useState<any>();
const forceUpdate = useForceUpdate();
const update = useCallback(() => forceUpdate(), []);
return (
//able to see updated state here when update() is called
//but execution is not going inside ChildA
//even use effects on updated state are not getting triggered
<ChildA
state = {state}
/>
<ChildB
update = {update}
/>
)
}
Existing hook:
const reducer = (state: boolean, _action: null): boolean => !state;
export const useForceUpdate = () => {
const [, dispatch] = useReducer(reducer, true);
// Turn dispatch(required_parameter) into dispatch().
const memoizedDispatch = useMemo(
() => () => {
dispatch(null);
},
[dispatch]
);
return memoizedDispatch;
};
When I changed the existing hook into the below format, this worked for me.
function useForceUpdate() {
const [, forceUpdate] = useReducer(x => x + 1, 0);
const memoizedUpdate = useMemo(
() => () => {
forceUpdate(0);
},
[forceUpdate]
);
return memoizedUpdate;
};
I have a functional component (App.js) where I want to fetch some initial data using useEffect.
useEffect(() => {
const init = async () => {
const posts = await getPosts(0, 3);
const newArticles = await getArticles(posts);
setArticles(() => [...articles, ...newArticles]);
};
init();
}, []);
then I want to pass the result to a child
<ArticleList articles={articles}></ArticleList>
but in the Article component I get an empty array when I try to console.log the props.
useEffect(() => {
console.log(props.articles);
setArticles(() => props.articles);
}, [props.articles]);
How can I solve this issue?
I want to build simple scroll pagination, so I update currentState with oldState and add new data to old state, meanwhile this new data comes from redux selector,
const Posts = () => {
const [page, setpage] = useState(1);
const [currentPosts, setCurrentPosts]: any = useState([]);
const dispatch = useDispatch();
const postList = useSelector((state: any) => state.posts);
const { loading = true, error, posts = [] }: IPostList = postList;
const handleClick = (id: number) => {
dispatch(listComments(id));
};
const handleScroll = (event: any) => {
const { scrollTop, clientHeight, scrollHeight } = event.currentTarget;
if (scrollHeight - scrollTop === clientHeight) {
setpage((prev) => prev + 1);
}
};
useEffect(() => {
dispatch(listPosts(page));
setCurrentPosts((prev: any) => [...prev, ...posts]);
}, [dispatch, page, posts]);
return (
<div onScroll={handleScroll} className="posts">
{posts.map((post, index) => (
<PostCard
onClick={() => handleClick(post.id)}
data={post}
key={index}
/>
))}
</div>
);
};
export default Posts;
I understand it causes an infinite loop because whenever posts are updated, listPosts are called, then it's updated again and again.
Can you provide me with the right solution and explanation on how to update currentPosts state while spreading the old state and new state which comes from the redux selector?
I think the code says more than my explanation.
Can you run this as two different effects? I believe this should eliminate the infinite loop.
useEffect(() => {
dispatch(listPosts(page));
}, [dispatch, page]);
useEffect(() => {
setCurrentPosts((prev: any) => [...prev, ...posts]);
}, [posts]);
I'm using react-router-dom v5, and react 16
Whenever I navigate like this:
from "/products/:someId" to "/products/:someOtherId"
the url changes but the components do not update accordingly
it's the same with queries.
"/products?search=something" or "/products?search=someOtherThing"
it does work when I'm in a different url, like "/" or "/users",
I'm using Link for navigation, I also tried the useHistory hook: history.push, history.replace and withRouter(myComponent);
This is how I'm getting data from queries/params
async function searchProducts(searchValue) {
const response = await axios.post(
"http://localhost:8000/api/products/search",
{ search: searchValue });
return response.data.body;
}
const useFetchData = (query) => {
const [products, setProducts] = useState([]);
useEffect(() => {
if (products.length === 0) {
// Use searchProducts for the request
searchProducts(query).then((foundProducts) => {
setProducts(foundProducts);
});
}
}, [products, query]);
return products;
};
then I useFetchData in my component which goes:
const ProductList = () => {
const history = useHistory();
// parsing query to be -> { search: "value" }
const urlQuery = queryString.parse(history.location.search);
const products = useFetchData(urlQuery.search);
const getList = () => {
return products.map((product) => {
return (
<li key={product._id}>
<ProductItem product={product} />
</li>
);
});
};
return <div className="container">{getList()}</div>;
};
The search button is in a different component for the header, it's always there since it's in the layout
<button className="header-search-button" onClick={handleClick}>
Search
</button>
and the handleClick:
// searchvalue has it's own onChange handler
const [searchValue, setSearchValue] = useState("");
// code...
const handleClick = () => {
// .... some code
// I also tried with push and Link
history.replace(`/products?search=${searchValue}`);
};
It's really difficult to tell without the code.
But my guess is you are able to change the /products?search=bag to /products?search=watch.
But after that it's not able to update the state and hence no re render.When you reload then the render happens.
It would be easier if we could see the code.