How to filter array with Hooks? - reactjs

I am currently working on a class-based function. Trying to convert the class to a stateless function, followed by refactoring my code for each event handler from this.SetState to useState (in this case setMovies).
This is my code (partial code):
const Movies = () => {
const [movies, setMovies] = useState(getMovies());
const [disabled, setDisabled] = useState(true);
const [pageSize, setPageSize] = useState(4);
const sortBy = sortType => {
setMovies(movies.filter(sortType));
setDisabled(false);
// this.setState({
// movies: this.state.movies.sort(sortType),
// isDisabled: false,
// });
};
it seems that It is not possible to filter this state. I am able to change to boolean but can't filter my Array. Is there a way to filter using Hooks?
Thanks in advance.

Nothing changes...
const List = () => {
const [items, setItems] = useState([1, 2, 3, 4, 5, 6])
const filterEvenResults = () => setItems(items => items.filter(x => x % 2))
return (
<div>
{
items.map(item => <p key={item}>{item}</p>)
}
<button onClick={filterEvenResults}>Filter</button>
</div>
)
}

Related

[solution]Why "Cards" still doesn't receive the passed value?

Why "Cards" still doesn't receive the passed value from selectedCountryInfo
I just tried passing await to the variable, still doesn't work. "Cards" still don't receive value.
<----solution: when there are have 2 setStates, should use 2 variables, not use 1 variable.(I guess if there are 3 setStates use 3 variables and so on)
I've been thinking about it for over 12 hours and can't think of a solution.
Because the default value of useState cannot put async/await.
(fetchedCountries is array,selectedCountryInfo is object)
const App = () => {
const [fetchedCountries, setFetchedCountries] = useState([]);
const [selectedCountryInfo, SetSelectedCountryInfo] = useState();
useEffect(() => {
const myCountries = async () => {
const countries = await worldWideCountries();
setFetchedCountries(countries);
SetSelectedCountryInfo(fetchedCountries[0]);
};
myCountries();
}, []);
return (
<div>
<Cards selectedCountryInfo={selectedCountryInfo} />
</div>
);
Solution:(from the 3 lines)
const countries = await worldWideCountries();
setFetchedCountries(countries);
const ww = countries[0];
SetSelectedCountryInfo(ww);
You probably want to use conditional rendering
const App = () => {
const [fetchedCountries, setFetchedCountries] = useState([]);
const [selectedCountryInfo, SetSelectedCountryInfo] = useState();
useEffect(() => {
const myCountries = async () => {
setFetchedCountries(await worldWideCountries());
SetSelectedCountryInfo(fetchedCountries[0]);
};
myCountries();
}, []);
return (
<div>
{ selectedCountryInfo && <Cards selectedCountryInfo={selectedCountryInfo} /> }
</div>
);
}

How to default some useState values and test the useState firing when there is a condition (state array should be equal to some value) on button click

Actual code :
const [success, setSuccess] = React.useState(false);
const [ingredients, setIngredients] = React.useState([]);
const [ingredient, setIngredient] = React.useState("");
<input data-test="app-input" type="text" value={ingredient} onChange={(event) =>
setIngredient(event.target.value)} />
<button data-test="app-button"
onClick={() => {
if (ingredient) {
if (ingredients.length === 2) {
setSuccess(true);
}
setIngredients([...ingredients, ingredient]);
}
}}>Add<button>
Test code below :
Here only the on change input value is called mockSetSuccess
test("success state updated when click of the button", () => {
const mockSetSuccess = jest.fn();
React.useState = jest.fn(() => [false, mockSetSuccess]);
const wrapper = shallow(<App />);
const input = wrapper.find("[data-test='app-input']");
const button = wrapper.find("[data-test='app-button']");
input.simulate("change", { target: { value: "hello" } });
button.simulate("click", {});
expect(mockSetSuccess).toHaveBeenCalledWith(true);
});
Can any body help to check the success state and the ingredients array state value

function component for state component

I would like to rewrite the function component into a state component. I have a problem with useCallback. Please help.
component function:
const App = () => {
const [elements, setElements] = useState([]);
const [history, setHistory] = useState([]);
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(0);
const handleOnHistoryPush = useCallback(() => {
setHistory([...history, elements]);
setElements([]);
}, [elements, history]);
const appendElement = useCallback(
value => setElements([...elements, value]),
[elements]
);
class component:
state = {
elements: [],
history: [],
currentHistoryIndex: 0
}
handleOnHistoryPush = useCallback(() => {
this.setState({history: ([...this.state.history, this.state.elements])});
this.setState({elements:([])});
}, [this.state.elements, this.state.history]);
appendElement = useCallback(
value => this.setState({elements:([...this.state.elements, value])}),
[this.state.elements]
);
this is a correct way to use state components callback function
state = {
elements: [],
history: [],
currentHistoryIndex: 0
}
handleOnHistoryPush = () => {
this.setState({history: ([...this.state.history, this.state.elements])});
this.setState({elements:([])});
};
appendElement = value => this.setState({elements:([...this.state.elements, value])});

combining useState hook in a react component

I recently started using react hooks a lot.
State management seems more intuitive to me when using "React.useState".
Anyway, while it's working ok, I know it's starting to look cluttered the more values I am starting to save to state.
For example, as my car parts app has progressed, it is now looking like this:
const [isShown, setIsShown] = React.useState(false);
const [idVal, setIdValue] = React.useState(false);
const [partNameVal, setPartNameValue] = React.useState(false);
const [engineEngineEngineTypeVal, setEngineEngineTypeValue] = React.useState(false);
const [displacementVal, setDisplacementValue] = React.useState(false);
const [makeVal, setMakeValue] = React.useState(false);
const [countryVal, setCountryValue] = React.useState(false);
const hide = () => setIsShown(false);
const show = (id, partName, engineEngineType, displacement, department, country) => {
setIsShown(true);
setIdValue(id);
setPartNameValue(partName);
setEngineTypeValue(engineEngineType);
setDisplacementValue(displacement);
setMakeValue(department);
setCountryValue(country);
}
<p>ID: {idVal}</p>
<p>PartName: {partNameVal}</p>
<p>EngineType: {engineEngineTypeVal}</p>
<p>Displacement: {displacementVal}</p>
<p>Make: {makeVal}</p>
<p>Country: {countryVal}</p>
I was wondering if there's a way to make this more readable, but still be very intuitive.
Thanks!
Typically you want to handle a single object or use useReducer, something like:
const INITIAL_CAR = {
id: 0,
part: "4xE3",
country: "USA",
// ... More entries
};
const CarApp = () => {
const [car, setCar] = useState(INITIAL_CAR);
const [isShown, setIsShown] = useState(false);
const show = (carProps) => {
setIsShown(true);
setCar(carProps);
};
const { id, part, engine, displacement, make, county } = car;
const updateCountry = (country) =>
setCar((prevCar) => ({ ...prevCar, country }));
const updateCarProperty = ({ property, value }) =>
setCar((prevCar) => ({ ...prevCar, [property]: value }));
return (
<div>
{isShown && (
<>
<p>ID: {id}</p>
<p>PartName: {part}</p>
<p>EngineType: {engine}</p>
<p>Displacement: {displacement}</p>
<p>Make: {make}</p>
<p>Country: {country}</p>{" "}
</>
)}
// use show, updateCountry, updateProperty etc.
</div>
);
};
I'd say that it's the case for useReducer hook.
https://reactjs.org/docs/hooks-reference.html#usereducer
const initialState = {
isShown: false,
idVal: 0,
....
};
function reducer(state, action) {
switch (action.type) {
case 'show':
return {
...state,
isShown: true,
idVal: action.payload.idVal
};
case 'hide':
return {
...state,
isShown: false
}
...
default:
throw new Error();
}
}
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({type: 'show', payload: { idVal: 1}})
The way I mostly handle this much of state in a component is using one useState, that way it's just a big object.
Here is a small example :
const [state, setState] = useState({
num: 1,
cars: ['volvo', 'mazda'],
john: {name: 'John', last: 'Foo'}
})
And if you want to change something in that I usually use this function
const onChange = (name, value) => {
setState(prevState => ({...prevState, [name]: value}))
}
This will change the key name to the value value. This is way clearer in my eyes.
You can make a new file to extract all your hook logic from your component.
Call if for example useHooks.js
export default () => {
const [isShown, setIsShown] = React.useState(false);
const [idVal, setIdValue] = React.useState(false);
const [partNameVal, setPartNameValue] = React.useState(false);
const [engineEngineEngineTypeVal, setEngineEngineTypeValue] = React.useState(false);
const [displacementVal, setDisplacementValue] = React.useState(false);
const [makeVal, setMakeValue] = React.useState(false);
const [countryVal, setCountryValue] = React.useState(false);
const hide = () => setIsShown(false);
const show = (id, partName, engineEngineType, displacement, department, country) => {
setIsShown(true);
setIdValue(id);
setPartNameValue(partName);
setEngineTypeValue(engineEngineType);
setDisplacementValue(displacement);
setMakeValue(department);
setCountryValue(country);
}
return [isShown, idVal, partNameVal, engineEngineEngineTypeVal, displacementVal,
makeVal, countryVal, show, hide];
}
The idea was here to put all your hooks logic in a function and return values that you need inside your JSX.
And in your component import and use all properties exported from useHooks
import useHooks from './useHooks';
const [isShown, idVal, partNameVal, engineEngineEngineTypeVal, displacementVal,
makeVal, countryVal, show, hide] = useHooks();
Hope the idea is clear

react hooks issue when infinite paging filter applied

I have an infinite paging setup in a react redux project like this..
const ItemDashboard = () => {
const items= useSelector(state => state.items.items);
const dispatch = useDispatch();
const [loadedItems, setLoadedItems] = useState([]);
const [categories, setCategories] = useState([
'cycling',
'diy',
'electrical',
'food',
'motoring',
'travel'
]);
const initial = useRef(true);
const [loadingInitial, setLoadingInitial] = useState(true);
const [moreItems, setMoreItems] = useState([]);
const onChangeFilter = (category, show) => {
!show
? setCategories(categories.filter(c => c != category))
: setCategories([...categories, category]);
};
const loadItems = () => {
dispatch(getItems(categories, items && items[items.length - 1]))
.then(more => setMoreItems(more));
}
const getNextItems = () => {
loadItems();
};
useEffect(() => {
if(initial.current) {
loadItems();
setLoadingInitial(false);
initial.current = false;
}
}, [loadItems]);
useEffect(() => {
if(items) {
setLoadedItems(loadedItems => [...loadedItems, ...items]);
}
}, [items]);
useEffect(() => {
//this effect is fired on intial load which is a problem!
setLoadingInitial(true);
initial.current = true;
}, [categories]);
return (
<Container>
<Filter onFilter={onChangeFilter} categories={categories} />
{loadingInitial ? (
<Row>
<Col sm={8} className='mt-2'>
<LoadingComponent />
</Col>
</Row>
) : (
<ItemList
items={loadedItems}
getNextItems={getNextItems}
moreItems={moreItems}
/>
)}
</Container>
);
};
In the filter component, when the filter is changed the onChangeFilter handler method is fired which updates the array of categories in state. When this filter is changed I need to set the loadedItems in state to an empty array and call the load items callback again but I can't work out how to do it. If I add another effect hook with a dependency on categories state, it fires on the initial load also. I'm probably doing this all wrong as it feels a bit hacky the whole thing. Any advice much appreciated.

Resources