I am coding a computer comparison website and I encountered an issue when setting filters for the API call. It seems to always be "one step behind" when I add a filter. For example, I add the filter for "orderby" and then call getComputers()
, which fetches the pcs from our api using the filters as parameters, but it doesn't apply any parameters, as the filters are still empty. If I do it again with a different value for "orderby" it takes the value from before as param for the api call. I don't understand why this works the way it does and would really appreciate any help :)
Yes, I did research on this before, but nothing seemed to work for me...
Here is my code:
The search results page:
import React from 'react';
import { useContext } from 'react';
import SearchContext from '../../context/search/SearchContext';
import Navbar from '../layout/Navbar';
import Computers from '../computer/Computers';
import Select from 'react-select';
const SearchResults = () => {
const searchContext = useContext(SearchContext);
const { filters, orders, getComputers, addFilter } = searchContext;
return (
<>
<Navbar activeItem='Search'></Navbar>
<div className='container my-3 text-dark'>
<div className='row'>
<div className='col-lg-9 col-md-6'>
<h1 className='display-3 mt-3'>Suche</h1>
</div>
<div className='col-lg-3 col-md-6'>
<Select
placeholder='Sortierung'
options={orders.map(order => {
return { value: order.value, label: order.trans };
})}
onChange={val => {
addFilter({ name: 'orderby', value: val.value });
getComputers();
}}
/>
</div>
</div>
<div className='my-3'>
{filters.map((filter, index) => {
var text = filter.value.toString();
return (
<span
className='badge badge-pill badge-secondary mx-1 p-2'
key={index}
>
{text.charAt(0).toUpperCase() + text.slice(1)}
</span>
);
})}
</div>
<Computers></Computers>
</div>
</>
);
};
export default SearchResults;
Strangely, my page displays the current "orderby" filter correctly in the little pills (see line 42). But the API call doesn't take the right filter...
Relevant parts of my Context:
SearchState.js:
const [state, dispatch] = useReducer(SearchReducer, initialState);
// ACTIONS
// Add filter
const addFilter = filter => {
console.log(state.filters);
dispatch({ type: ADD_FILTER, payload: filter });
};
// Get PCs from API
const getComputers = async () => {
try {
state.loading = true;
console.log(state.filters);
let params = {};
state.filters.forEach(filter => {
// if (filter.name !== 'usecase' && filter.name !== 'type')
//only as long as usecase and type are not implemented in api yet
if (!params[filter.name]) params[filter.name] = '';
params[filter.name] = filter.value;
});
console.log(params);
const res = await axios.get(
'notgonnatellumyurlxd',
{
params
}
);
console.log(res);
dispatch({ type: GET_COMPUTERS, payload: res.data });
} catch (err) {
console.log(err);
}
};
SearchReducer.js:
export default (state, action) => {
switch (action.type) {
case ADD_FILTER:
// Used for deciding whether to update existing filter or set new
var update = false;
state.filters.forEach(filter => {
if (filter.name === action.payload.name) {
update = true;
}
});
return {
...state,
filters: !update
? [...state.filters, action.payload]
: state.filters.map(filter =>
filter.name === action.payload.name ? action.payload : filter
)
};
.
.
.
case GET_COMPUTERS:
return {
...state,
computers: action.payload,
loading: false
};
};
I am really struggling with this right now and hope to find some help here. Big thanks in advance! You people on here are great :)
getComputers
works on other state
"instance"/ref`
You can use useEffect
as addFilter
changes filter
:
useEffect(() => {
getComputers();
}, [filter]);
onChange={val => addFilter({ name: 'orderby', value: val.value })}
addFilter
changes filter
... filter
change runs effect code (every time it changes)