How to organize useState and useQuery - Apollo/Graphql - reactjs

I'm trying to pass data from useState([]) to the variables for filtering products. It works, but on very strange way.
When I click on a category checkbox, it is not checked, but the products are filtered, when I click on another category, again checkbox didn't checked, but already two values in the array useState([1,2]), and when I click again, the checkbox is checked, but is removed from the array useState([]).
const GET_PRODUCTS = gql`
query GetProducts($filterByCategory: [ID]) {
products(filters: {category: {id:{in: $filterByCategory}}}) {
....
}
}
`
const VacanciesPage = () => {
const [selectCategories, setSelectedCategories] = useState([]);
const getSetSelectedCategories = (category) => {
console.log(category);
if(selectCategories.includes(category)){
setSelectedCategories(selectCategories.filter(item => item != category));
return;
}
setSelectedCategories([...selectCategories, category]);
}
useEffect(() => {
console.log(selectCategories);
},[selectCategories]);
const { loading, error, data } = useQuery(GET_PRODUCTS, {
variables: { "filterByCategory": selectCategories},
});
if (loading) return null;
if (error) return `Error! ${error}`;
const { products, categories } = data;
return(
{categories.data.map(category => (
<label key={category.id} className="inline-flex items-center mt-3 mr-3">
<input type="checkbox" className="w-5 h-5" value={category.id} onChange={e => getSetSelectedCategories(+e.target.value)}/><span className="ml-2 text-gray-700">{category.attributes.name}</span>
</label>
))}
);
}

I completely rewrote the code, moved the variables to a separate function and everything finally worked.
const [selectedCategories, setSelectedCategories] = useState([]);
const [searchStatus, setSearchStatus] = useState(false);
useEffect(() => {
console.log(selectedCategories);
},[selectedCategories]);
const { loading, error, data } = useQuery(GET_PRODUCTS);
const { loading: loadingCategories, error: errorCategories, data: categories } = useQuery(GET_CATEGORIES);
const [ getFilteredProducts,
{ loading: filterLoading, error: filterError, data: filteredProducts }
] = useLazyQuery(FILTER_PRODUCTS_QUERY);
if (loading || loadingCategories || filterLoading) return "Loading";
if (error || errorCategories || filterError) return `Error! ${error}`;
const { products: allProducts } = data;
const { categories: allCategories } = categories;
const clearSearch = () => {
setSelectedCategories([]);
setSearchStatus(false);
};
const searchProducts = () => {
let filter = {};
setSearchStatus(true);
if (Object.keys(filter).length === 0) {
if (!selectedCategories) {
setSearchStatus(false);
return;
}
}
getFilteredProducts({
variables: {
filterByCategories: selectedCategories
? selectedCategories
: allCategories.data.map((category) => category.id),
},
});
};
const dataset = searchStatus && filteredProducts ? filteredProducts?.products : allProducts;

Related

why console gives in this sequence?

I am making a mern ecommerce website i just want to see how useEffect works so i console.log in some part of useEffect and loadFilteredResults i saw that --->
initial
entry
skip
entry1
screen shot
but i think it shoud be-->
initial
entry
entry1
skip
why console give this?? i am a begginer, i am a self learner , so please if you need any extra info please comment.
code snippet-->
const loadFilteredResults = (newFilters) => {
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
....
....
useEffect(() => {
init();
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
//full code of shop.js
import React, { useEffect, useState } from "react";
import Layout from "./Layout";
import Card from "./Card";
import { getCategories, getFilteredProducts } from "./apiCore";
import Checkbox from "./Checkbox";
import RadioBox from "./RadioBox";
import { prices } from "./fixedPrices";
const Shop = () => {
const [myFilters, setMyFilters] = useState({
filters: { category: [], price: [] }
});
const [categories, setCategories] = useState([]);
const [error, setError] = useState(false);
const [limit, setLimit] = useState(3);//prpduct lesss so use 3 but sir used 6
const [skip, setSkip] = useState(0);
const [size, setSize] = useState(0);
const [filteredResults, setFilteredResults] = useState([]);
const init = () => {
getCategories().then((data) => {
if (data.error) {
//console.log("error");
setError(data.error);
} else {
//console.log("set");
//console.log(data);
setCategories(data);
//console.log(data);
}
});
};
const loadFilteredResults = (newFilters) => {
//console.log(newFilters);
console.log("entry")
getFilteredProducts(skip, limit, newFilters).then((data) => {
console.log("entry1")
if (data.error) {
setError(data.error);
} else {
//console.log(data);
setFilteredResults(data.data);
//console.log("size-->");
//console.log(data.size);
setSize(data.size);
setSkip(0);
}
});
};
const loadMore = () => {
console.log("skip"+skip);
console.log("limit"+limit);
let toSkip = skip + limit;
console.log("toSkip"+toSkip);
getFilteredProducts(toSkip, limit, myFilters.filters).then((data) => {
//console.log("filter");
//console.log( myFilters.filters)
if (data.error) {
setError(data.error);
} else {
//console.log(filteredResults);
//console.log(data.data);
setFilteredResults([...filteredResults, ...data.data]);
//console.log("after");
//console.log(...filteredResults);
//console.log(filteredResults);
//console.log(filteredResults);
//console.log([...filteredResults])
//console.log([...filteredResults, ...data.data])
setSize(data.size);
setSkip(toSkip);
}
});
};
const loadMoreButton = () => {
return (
size > 0 &&
size >= limit && (
<button onClick={loadMore} className="btn btn-warning mb-5">
load more
</button>
)
);
};
useEffect(() => {
init();
//console.log(skip);
console.log("initial");
loadFilteredResults(skip, limit, myFilters.filters);
console.log("skip");
}, []);
const handleFilters = (filters, filterBy) => {
//console.log("SHOP", filters, filterBy);
const newFilters = { ...myFilters };
//console.log(newFilters);
newFilters.filters[filterBy] = filters;
//console.log(typeof(filters));
if (filterBy === "price") {
let priceValues = handlePrice(filters);
newFilters.filters[filterBy] = priceValues;
//console.log(priceValues);
}
//console.log(myFilters.filters);
loadFilteredResults(myFilters.filters);
setMyFilters(newFilters);
};
const handlePrice = (value) => {
const data = prices;
let array = [];
//console.log(value);
for (let key in data) {
if (data[key]._id === parseInt(value)) {
array = data[key].array;
}
}
return array;
};
// const x = (filters)=>{
// console.log("filters:"+filters);
// handleFilters(filters, "category")
// }
return (
<Layout
title="Shop Page"
description="search and buy books of your choice"
className="container-fluid"
>
<div className="row">
<div className="col-4">
<h4>Filter by categories</h4>
<ul>
{/* below will be show in list show we wrap it in unorder list */}
<Checkbox
categories={categories}
handleFilters={(filters) =>
handleFilters(filters, "category")
}
/>
</ul>
<h4>Filter by price range</h4>
<div>
<RadioBox
prices={prices}
handleFilters={(filters) => handleFilters(filters, "price")}
/>
</div>
</div>
<div className="col-8">
<h2 className="mb-4">Products</h2>
<div className="row">
{filteredResults.map((product, i) => (
<Card key={i} product={product} />
))}
</div>
<hr />
{loadMoreButton()}
</div>
</div>
</Layout>
);
};
export default Shop;
getFilteredProducts must be a Promise. Please read Using promises
Callbacks added with then() will never be invoked before the
completion of the current run of the JavaScript event loop.

React - autocomplete search for API - array always returns empty

I'm trying to make an autocomplete search for Alpha Vantage API, but the array which should contain the matches (suggestion) always returns empty when I type in the input field and I don't know what could be the reason, I'm stuck for a while on this and would appreciate if someone could help me with this.
The related code here is mostly in useEffect and the inputHandler:
const Search = () => {
useEffect(() => {
const getSymbols = async () => {
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const res = await axios.get(searchURL);
if(res) {
setSecurity(res.data.bestMatches);
if(security !== undefined && security.length > 0) {
let symbols = security.map(sec => sec['1. symbol'])
setAllSymbol(symbols);
}
}
}
getSymbols();
}, [])
console.log(allSymbol);
const inputHandler = (e) => {
setTextInput(e.target.value);
let matches = [];
if(textInput.length > 0) {
matches = allSymbol.filter(sym => {
const regex = new RegExp(`${textInput}`, "gi");
return sym.match(regex);
})
setSuggestion(matches);
}
console.log(suggestion);
setTextInput(e.target.value);
}
const showData = async (e) => {
e.preventDefault();
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const monthlyURL = `https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
try {
const res = await axios.get(searchURL);
const data = await axios.get(monthlyURL);
if(res) {
setTickers(res.data.bestMatches[0]);
setSymbol(res.data.bestMatches[0]['1. symbol']);
setSecurity(res.data.bestMatches);
if(data) {
const monthlyTimeSeries = Object.values(data.data['Monthly Time Series']);
const result = [monthlyTimeSeries[1]];
const resultValues = Object.keys(result[0]).map(key => {
return Math.floor(result[0][key]);
})
setPrices(resultValues);
}
}
} catch(err) {
console.log(err)
}
setDailyPrices([]);
setWeeklyPrices([]);
setIntraPrices([]);
}
return (
<StyledSearch>
<div className="wrapper">
<h1>Security Price Monitor App </h1>
<form onSubmit={showData} className="search-form">
<input type="text" value={textInput} onChange={inputHandler} placeholder='Enter Stock Symbol (GOOG, MSFT)'/>
<button type="submit">Search</button>
</form>
</div>
{prices.length > 0 && (
<>
<Table prices={prices} symbol={symbol}/>
<TimeFrames symbol={symbol} textInput={textInput} weeklyPrices={weeklyPrices} setWeeklyPrices={setWeeklyPrices} dailyPrices={dailyPrices} setDailyPrices={setDailyPrices} intraPrices={intraPrices} setIntraPrices={setIntraPrices} />
</>
)}
</StyledSearch>
)
}

React - API data only showing if I manually refresh the page

I'm new to React and I created a small admin panel where you can add, edit, remove products. I would like to display 3 products from API when someone opens the app the first time and don't have edited products yet, but this data only shows if I manually refresh the page. I only want to display that if edited product is false, but initially I set edited products to false yet somehow it's not displaying, though I see the data as well as edited is set to false in the console.
Demo
https://react-storeadminpanel.herokuapp.com/
Here is the related code:
const Products = () => {
const {products, setProducts, setAllProducts, allProducts, editedItems, setEditedItems} = useProduct();
useEffect(() => {
async function fetchProducts() {
const res = await axios.get('https://a.nacapi.com/LimeGreen/products/').catch(err => console.log(err));
if(res) {
setProducts(res.data)
setEditedItems(false);
if(allProducts.length === 0 && editedItems === false) setAllProducts(products);
if(allProducts.length === 0 && editedItems === true) setAllProducts(allProducts);
if(allProducts.length > 0) setAllProducts([...allProducts]);
}
return res;
}
fetchProducts();
}, []);
return (
<Wrapper classname="wrapper">
<h1>All Products</h1>
<Cards>
{!!allProducts.length && (
allProducts.map(product => (
<ProductCard name={product.name} description={product.Description} price={product.Price} discount={product.Discount} key={product.uuid}/>
))
)}
</Cards>
</Wrapper>
)
}
The context, where I use LocalStorage
export const ProductContext = React.createContext();
export function useProduct() {
return useContext(ProductContext);
}
export function ProductProvider({children}) {
const [products, setProducts] = useLocalStorage('Api Data', []);
const [addedProduct, setAddedProduct] = useLocalStorage('Added Item', []);
const [allProducts, setAllProducts] = useLocalStorage('All Products', []);
const [editedItems, setEditedItems ] = useLocalStorage('Edited', false);
const [isAdded, setIsAdded] = useState(false);
const value = {
products,
setProducts,
addedProduct,
setAddedProduct,
allProducts,
setAllProducts,
editedItems,
setEditedItems,
isAdded,
setIsAdded,
}
return (
<ProductContext.Provider value={value}>
{children}
</ProductContext.Provider>
)
}
And Code where I set edit products to true
const ProductEdit = () => {
const {allProducts, setAllProducts, setEditedItems} = useProduct();
const [editProductId, setEditProductId] = useState(null);
const [editForm, setEditForm] = useState({
name: "",
Description: "",
Price: "",
Discount: "",
})
const saveEditHandler = (e) => {
e.preventDefault();
const fieldName = e.target.getAttribute("name");
const fieldValue = e.target.value;
const newForm = {...editForm};
newForm[fieldName] = fieldValue;
setEditForm(newForm);
}
const editHandler = (e, product) => {
e.preventDefault();
setEditProductId(product.uuid);
const formValues = {
name: product.Name,
Description: product.Description,
Price: product.Price,
Discount: product.Discount
}
setEditForm(formValues);
}
const submitEditsHandler = (e) => {
e.preventDefault();
const editedProduct = {
name: editForm.Name,
Description: editForm.Description,
Price: editForm.Price,
Discount: editForm.Discount,
uuid: editProductId
}
const newProducts = [...allProducts];
const index = allProducts.findIndex((product) => product.uuid === editProductId);
newProducts[index] = editedProduct;
setAllProducts(newProducts);
setEditedItems(true);
setEditProductId(null);
}
const cancelHandler = () => {
setEditProductId(null);
}
const deleteHandler = (productId) => {
const newProducts = [...allProducts];
const index = allProducts.findIndex((product) => product.uuid === productId);
newProducts.splice(index, 1);
setAllProducts(newProducts);
setEditedItems(true);
};

I have a question about react array clearing

There is an array in the parent class(TodolistActivity), and the child class(TaskCallFunc) displays the elements in the array. When I use a.list= []; to clear the array, there is no clearing on the page
but a.list.length = 0 is ok. why?
Here is my code:
interface IListData {
list: IActivityData[]
}
interface IActivityData {
id: number,
content: string,
finish: boolean
}
export function TodolistActivity(activty: IListData) {
const [acty, setActivity] = useState(activty);
const [content, setContent] = useState('');
const input_ref = React.createRef<HTMLInputElement>();
const [selectCount, setSelect] = useState(0);
const handleAdd = () => {
if (input_ref.current) {
if (input_ref.current.value === '') {
alert("输入内容 不能为空!");
return;
}
let id = acty.list.length;
acty.list.unshift({ id: id, content: content, finish: false })
let a = { ...acty }
setActivity(a);
input_ref.current.value = "";
}
}
const calcuateSelect = () => {
let a = acty.list.filter((v, i) => { return v.finish === true })
setSelect(a.length);
}
const handleChange = (input: React.ChangeEvent<HTMLInputElement>) => {
setContent(input.target.value);
}
const clearTask = () => {
let a = { ...acty};
a.list= [];
//a.list.length = 0;
setActivity(a);
}
return (
<div>
<input type='text' onChange={handleChange} ref={input_ref} />
<button className="add task" onClick={handleAdd}>add task</button>
<button className="clear task" onClick={clearTask}>clear task</button>
{console.log(acty)}
<TaskCallFunc data={acty} action={() => { calcuateSelect() }} />
<br />
<label htmlFor="">select{selectCount}/{acty.list.length}</label>
</div>
);
}
interface ItaskCell {
data: IListData,
action: () => void
}
function TaskCallFunc(taskData: ItaskCell) {
const [data, setData] = useState(taskData);
const HandleSlecet = (x: number) => {
for (let index = 0; index < data.data.list.length; index++) {
if (data.data.list[index].id === x) {
let newState = { ...data };
newState.data.list[index].finish = !data.data.list[index].finish;
setData(newState);
data.action();
}
}
}
const handleMap = () => {
return data.data.list.map((v, i) => { return <li key={v.id}>{v.id}: {v.content} <input type="checkbox" checked={v.finish} onChange={() => { HandleSlecet(v.id) }} /> </li> });
}
return (
<ul>{handleMap()}</ul>
);
}
If you know the answer, please let me know thank you
TaskCallFunc component doesn't "listen" for changes on the taskData prop to update the local copy stored in state. Use an useEffect hook with a dependency on taskData prop to update the state when it changes.
function TaskCallFunc(taskData: ItaskCell) {
const [data, setData] = useState(taskData);
useEffect(() => {
setData(taskData);
}, [taskData]);
...
You can clear the array easely by doing setActivity({ list: [] }) consider also to add useEffect as Drew says to listen for changes

When I use async in componentDidMount, Component will Mount and Unmount again and again. Why?

When I call getCityName component will unmount and DidMount again and again, unless I remove async .All the code is running in nextjs.
this.state = {
bank_account: {
// bank_name: '',
// number: '',
// city: '',
// branch_name: ''
},
allCity: []
};
componentDidMount() {
const { owner_cellphone } = this.props;
this.getDraft(owner_cellphone);
this.fetchCity();
}
fetchCity = async () => {
const { data, error } = await getCity();
if (error) {
return;
}
console.log(data);
this.setState({ allCity: data });
};
getCityName = cityString => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render() {
const { bank_account } = this.state;
const cityValue = this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
The reason it's not working because you are calling a async function from a sync function.
I am not sure it would work, but you can try..
getCityName = async (cityString) => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render = async () => {
const { bank_account } = this.state;
const cityValue = await this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
getCityName = cityString => {
const { allCity } = this.state;
if (allCity === [] || !cityString) {
return;
}
const cityArray = cityString.split(' ');
let targetProvince = allCity.find(item => item.code === cityArray[0]);
if (targetProvince) {
let newProvince = JSON.parse(JSON.stringify(targetProvince));
const targetCity = newProvince.children.find(item => item.code === cityArray[1]);
return `${targetProvince.name} ${targetCity.name}`;
}
return '';
};
I think it might be a problem of deep copy.

Resources