I have custom hook useFetch that takes URL as input and returns 3 data and I have Component CompanyListing to display those data. I want to send two different values as a URL in that hook using if else statement. If no search is done by user then display data from if endpoint, and if user search by keyword, then display from else endpoint.
This is code that I wrote CompanyListing.js
const [counter, setCounter] = useState(1);
let endpoint = `${CONSTANTS.BASE_URL}/companies?page=${counter}`;
const searchCompanies = (searchTerm) => {
if (searchTerm === '') {
endpoint = `${CONSTANTS.BASE_URL}/companies?page=${counter}`;
//console.log('DEFAULT ENDPOINT', endpoint);
} else {
endpoint = `${CONSTANTS.BASE_URL}/companies?search=${searchTerm}`;
//console.log('SEARCH ENDPOINT', endpoint);
}
};
//console.log('USEFETCH ENDPOINT', endpoint);
const [companies, isLoading, meta] = useFetch(endpoint);
return (
<>
<div data-testid="search-box">
<SearchCompany callback={searchCompanies} />
</div>
{!isLoading ? (
<div className="container">
<div className="row" data-testid="company-list">
{companies.length !== 0 && companies
? companies.map((company) => <CompanyLists key={company.id} {...company} data-testid="company-list" />)
: 'No companies'}
</div>
<Pagination meta={meta} counter={counter} setCounter={setCounter} data-testid="paginate" />
</div>
) : (
<Spinner />
)}
</>
);
};
export default CompanyListing;
When I tried calling useFetch inside if else, it goes against the rules of hooks and when I tried doing like in code above, I can't pass search endpoint to useFetch. Any idea how can I fix it? I tried by making all fetch on same component and it works perfectly but already having custom hook, I don't want to repeat code again.
And any idea why I am getting 5 console for same thing ?
Help will be much appreciated.
Issue I think is that, your parent component is not being re-rendered when searchCompanies is called so as result useFetch won't get called, and also you can't use that inside the if else also,
So, I think you can do something like this, maintain the endpoint in state and change it when ever searchCompanies is being called, in that way your component will be re-rendered and you will get the latest endpoint.
const [counter, setCounter] = useState(1);
const [endpoint,setEndPoint] = useState(`${CONSTANTS.BASE_URL}/companies?page=${counter}`);
const searchCompanies = (searchTerm) => {
if (searchTerm === '') {
setEndPoint(`${CONSTANTS.BASE_URL}/companies?page=${counter}`);
} else {
setEndPoint(`${CONSTANTS.BASE_URL}/companies?search=${searchTerm}`);
}
};
//console.log('USEFETCH ENDPOINT', endpoint);
const [companies, isLoading, meta] = useFetch(endpoint);
Related
I'm dynamically adding instances of a custom (Kendo-React) component into an array in my main App.
The component:
const PersonDD = () => {
const ages = ["Child", "Adult", "Senior"];
return (
<div>
<div>Person:</div>
<DropDownList
data={ages} style={{ width: "300px", }}
/>
</div>
);
};
I'm adding one instance on initial render, and another two instances after the result from an Ajax call returns.
const SourceTab = (SourceTabProps) => {
....
var componentList = [];
componentList.push(<PersonDD/>);
async function getStrata(){
var url = '/access/.im.read';
const res = await axios.get( url );
console.log(res.data.item);
componentList.push(<PersonDD/>);
componentList.push(<PersonDD/>);
}
React.useEffect(() =>{
getStrata();
},[]);
return (
<Title title="People" />
<div className='assignment_div_css'>
{componentList}
</div>);
};
The problem I have is that the one instance in the initial array are rendered, but the two created after the Ajax call are not rendered.
Do I need to call .render() or something similar to refresh?
You can simply use react useState to rerender component and in jsx map them.
like this :
const SourceTab = (SourceTabProps) => {
const [componentList,setComponentList] = useState([PersonDD])
async function getStrata(){
var url = '/access/.im.read';
const res = await axios.get( url );
console.log(res.data.item);
setComponentList([...componentList,PersonDD,PersonDD])
}
React.useEffect(() =>{
getStrata();
},[]);
return (
<Title title="People" />
<div className='assignment_div_css'>
{componentList.map((Component,index)=> <Component key={index} />)}
</div>);
};
You need to remember that React only re-renders (refreshes the UI/view) when a state changes. Your componentList is not a state at the moment but just an ordinary variable. make it a state by using useState hook.
Not sure if it is a bad practice or not but I haven't seen any react project that keeps an entire component as a state so instead of creating a state with an array of components, just push a data representation of the components you want to render. Then display the component list using your list and using .map
Here's how it would look like.
....
const [personList, setPersonList] = useState([1]);
async function getStrata(){
var url = '/access/.im.read';
const res = await axios.get( url );
setPersonList(state => state.push(2)); //you can make this dynamic so it can rerender as much components as you like, for now im pushing only #2
}
React.useEffect(() =>{
getStrata();
},[]);
return (
<Title title="People" />
<div className='assignment_div_css'>
{personList.map((item, key) => <PersonDD key={key} />)}
</div>);
};
Need to use the map to render a list
<div className='assignment_div_css'>
{componentList.map(component => <>{component}</>)}
</div>);
also, use a usestate to variable
const [componentList , setComponentList ]= React.useState[<PersonDD/>];
inside function set like this
console.log(res.data.item);
setComponentList(state => [...state, <PersonDD/>, <PersonDD/>]);
Using ReactJS, Firestore - Firebase v9.
I have an autocomplete search bar on my web, built with just pure React. The question that I am trying to solve for at least one month is, if I can make this autocomplete input work with Firestore - (user type E.g 'elepha', auto suggestion appears with offer with word elephant - this is what I coded, and with same case, user will click on the suggestion of elephant, and this word elephant will be send to Firestore.)
Cuz there is not any solution on internet, I wonder, if my question is even possible to make, or not.
My simple autocomplete bar code - (animals_data stands for file with animals names)
and I tried to add onClick={pleaseSend} which is basic addDoc function, but when I click on the suggestion, in Firestore will only appear blank input "".
<SearchBar data={animals_data} />
And the filtering code:
function SearchBar({ placeholder, data }) {
const [filteredData, setFilteredData] = useState([]);
const [wordEntered, setWordEntered] = useState("");
const [newAnswer, setAnswer] = useState("")
const [users, setUsers] = useState([]);
const usersCollectionRef = collection(db, "Answers")
const createUser = async () => {
await addDoc(usersCollectionRef, {name: newAnswer}).then(()=>{
window.location.reload()
}).catch((err)=>{
console.log(err)
})
};
const handleFilter = (event) => {
const searchWord = event.target.value;
setWordEntered(searchWord);
const newFilter = data.filter((value) => {
return value.full_name.toLowerCase().includes(searchWord.toLowerCase());
});
if (searchWord === "") {
setFilteredData([]);
} else {
setFilteredData(newFilter);
}
};
const clearInput = () => {
setFilteredData([]);
setWordEntered("");
};
return (
<div className="search">
<div className="searchInputs">
<input
type="text"
placeholder={placeholder}
value={wordEntered}
onChange={handleFilter}
/>
</div>
{filteredData.length !== 0 && (
<div className="dataResult">
{filteredData.slice(0, 15).map((value, key) => {
return (
<a className="dataItem" onClick={createUser} target="_blank">
<p>{value.full_name} </p>
</a>
);
})}
</div>
)}
</div>
);
}
export default SearchBar;
EDIT 1: added code screenshots
web:
Thank you very much for reading.
after analyzing your code, I notice that you don't update the value newAnswer using its setter. Therefore, you should use the setter to update the state on user click and then add the firestorm document. You can do that by either using a button/unmodifiable input field in instead of an anchor tag to store the value of each option, then use this value inside the click handler to update the state and then use a useEffect to update firestore every time the state changes. Let me know if you need help with some code. Please post it below your original question as an edit without changing it.
I have no idea why, the first render shows an empty object and the second shows my data:
function RecipeList(props) {
return (
<div>
{console.log(props.recipes)}
{/*{props.recipes.hits.map(r => (*/}
{/* <Recipe initial="lb" title={r.recipe.label} date={'1 Hour Ago'}/>*/}
</div>
)
}
const RECIPES_URL = 'http://cors-anywhere.herokuapp.com/http://test-es.edamam.com/search?i?app_id=426&q=chicken&to=10'
export default function App() {
const classes = useStyles();
const [data, setData] = useState({});
useEffect(() => {
axios.get(RECIPES_URL)
.then(res => {
setData(res.data);
})
.catch(err => {
console.log(err)
})
}, []);
return (
<div className={classes.root}>
<NavBar/>
<RecipeList recipes={data}/>
<Footer/>
</div>
);
}
I don't know why and I have struggled here for over an hour (React newbie), so I must be missing something.
This is the expected behavior. The reason you see two console logs is because, the first time RecipeList is called with no data (empty object), and the second time when the data becomes available. If you would like to render it only when the data is available you could do something like {Object.keys(data).length > 0 && <RecipeList recipes={data}/>}. By the way this is called conditional rendering.
This is perfectly normal, React will render your component first with no data. Then when your axios.get returns and update data, it will be rendered again with the new data
I want to apply React hook for infinite scroll. So, I end up with this code:
export default function index() {
const [allTracks, setAllTracks] = useState([]);
const [offset, setOffset] = useState("");
const { tracks, error, loading, lastVisible } = useFetchPublicTracks(
myApiEndPoint.TRENDING_TRACKS,
5,
offset
);
//concat tracks when got data successfully
useEffect(() => {
if (!loading && tracks) setAllTracks([...allTracks, ...tracks]);
}, [loading, tracks]);
console.log("tracks", allTracks);
console.log("lastVisible", lastVisible);
console.log("loading", loading);
return (
<div>
<button className="" onClick={() => setOffset(lastVisible)}>
Load More
</button>
<Main></Main>
</div>
);
}
When I click "Load More" button, new offset will be setted. After that, component re-render again and call Api with new offset.
I want ask that I am using React hook correctly ? There is any way better ? Also, Do I need to use useCallback/useMemo in my use case ?
im calling an api that does a fetch , but when using setHotSalesArray, the hotSalesArray is empty after useEffect Finishes
i have tried calling another function sending the data, also tried putting the data inside a variable to try and update it that way
here is the part im having trouble with
const [isActive, setIsActive] = useState(true);
const [hotSalesArray, setHotSales] = useState({});
useEffect(()=>{
let infoData;
API.getToken().then(
(data) =>API.getHotSale(data).then(info => (
infoData = info,
setHotSales(infoData),
setIsActive(false),
console.log(hotSalesArray,info, infoData)
),
)
)
},[])
this is what i get on the console :
{} {hotsales: Array(1)} {hotsales: Array(1)}
and the result im expecting is this:
{hotsales: Array(1)} {hotsales: Array(1)} {hotsales: Array(1)}
Update:
so i learned that i need a re-render to happen so i can have my new value inside the variable, my problem is i have this in my return
<div className="hotSalesContainer">
<h1 className="activitiesLabelHot">
Ventas Calientes
</h1>
{hotSales}
{ hotSales !== undefined ? hotSales.map((hotsale,index) => <PropertyElement data={hotsales} key={index.toString()} /> ) : ""}
</div>```
so i need the object hotSales to have something before i return that, i used to do this call before with componentWillMount, but im trying to learn how to use hooks
When you do console.log(hotSalesArray, info, infoData), hotSalesArray will have the value of the current render, which is {} (the initial value). It won't be until a re-render occurs that hotSalesArray will have the value of { hotsales: [...] }, but you won't see that in the console because you're only logging when the response comes back from your API.
Add a console log directly in the render and you'll see.
I fixed the syntax errors and did some cleanup. Besides the syntax errors though, you had the right idea. This could be improved upon with useReducer perhaps, but I didn't want to overcomplicate the answer :)
const MyComponent = () => {
const [isActive, setIsActive] = useState(true);
const [hotSales, setHotSales] = useState({});
useEffect(
() => {
API.getToken()
.then(data => API.getHotSale(data))
.then(info => {
setHotSales(info);
setIsActive(false);
})
},
[]
);
const { hotsales = [] } = hotSales;
return (
<div className='hotSalesContainer'>
<h1 className='activitiesLabelHot'>Ventas Calientes</h1>
{ hotsales.length &&
hotsales.map((sale, index) =>
<PropertyElement data={sale} key={index} />
)
}
</div>
)
}