Dynamic api change - reactjs

hello this is my first question, I wanted to create a dynamically changing link api based on e.target with select. I need the option value to be added to api as a number. I tried to use parseInt but unsuccessfully. the first time I get the message: Parameter 'matchday' is expected to be an integer in the range 1-46. After choosing another option, everything starts loading as it should.
Component
import React, { useEffect } from "react";
const Fixtures = ({ fixtures, getFixtures, loading }) => {
useEffect(() => {
getFixtures();
}, [getFixtures]);
const handleOnChange = e => {
getFixtures(e.target.value);
};
return (
<>
<select onChange={handleOnChange}>
<option value="1">Matchday 1</option>
<option value="2">Matchday 2</option>
</select>
</>
);
};
export const getFixtures = matchday => dispatch => {
dispatch(startFetchingFixtures());
const getFixturesUrl = matchday =>
`http://api.football-data.org//v2/competitions/2021/matches?matchday=${matchday}`;
fetch(getFixturesUrl(matchday), {
headers: {
"X-Auth-Token": "..."
}
}).then(response => response.json())
.then(response => response.matches)
.then(matches => dispatch(fetchedFixtures(matches)));
};

Related

React-Query: How to refetch useQuery with different params without using state?

I have one issue with fetching the data using the useQuery. Please have a look at the code below:
FetchUser hook:
const useFetchUsers = ({ selectedSchool, selectedYear }) => {
return useQuery(['Users', selectedSchool, selectedYear], async () => {
const URL = getSchoolURL({ selectedSchool, selectedYear })
const response = await fetch(URL)
const data = await response.json()
return {
count: data.count,
users: data.users
}
}, {
enabled: !!(selectedSchool && selectedYear),
onSuccess: () => {
console.log('success')
},
onError: () => {
console.log('errors')
}
})
}
Users component:
const Users = () => {
const {
isLoading,
data,
isError
} = useFetchUsers({ selectedSchool: '', selectedYear: '' })
const updateUsersData = ({ selectedSchool, selectedYear }) => {
// Here, I have to write logic to fetch Users data as per
// selected organization and selectedYear
}
return (
<div className='app'>
<Schools updateUsersData={updateUsersData}/>
{
/**
* Rendering components
* <Component-1/>
* <Component-2/>
* <Component-3/>
* <Component-4/>
* <Component-5/>
*
*/
}
</div>
)
}
School component:
const Schools = () => {
const [school, setSchool] = useState('')
const handleChange = (e) => {
const selectedSchool = e.target.value
setSchool(selectedSchool)
if (selectedSchool) {
// we have other logic to select selected Year
// but here sake for the example, I'm using this value
// hardcoded
updateUsersData({ selectedSchool, selectedYear: '2021' })
}
}
return (
<select
value={school}
onChange={handleChange}
name='school'
id='school'>
<option
value={''}
key={'Select School'}>
Select School
</option>
<option value={'school-1'}>school-1</option>
<option value={'school-2'}>school-2</option>
<option value={'school-3'}>school-3</option>
<option value={'school-4'}>school-4</option>
<option value={'school-5'}>school-5</option>
</select>
)
}
Some notes:
School component: Here, we are rendering the school names and when the user selects any school data, we are calling updateUsersData method and from this method, we have to call again the useFetchUsers hook with updated params but it is not working.
I don't want to take additional states i.e selectedSchool and selectedYear on Users component because of unnecessary component rendering.
Problem: How to again call useFetchUsers hook with updated params from updateUsersData method?

Using bootstrap-select within React

I want to dynamically add options to a bootstrap-select drop down selector. Options that are added manually work fine, the dynamic ones don't. Below "float 1" is an option in the selector, but the others that a provided by the API don't. If I remove "selectpicker" all options show up.
import { useState, useEffect } from "react";
const SelectFloats = () => {
const [selectedFloats, setFloat] = useState([]);
const [floats, setFloats] = useState([]);
useEffect(() => {
requestFloats();
}, []);
//Get list of floats for selector
async function requestFloats() {
const res = await fetch(`http://127.0.0.1:8000/ajax/get_deployments_list`, {
mode: "cors",
});
const json = await res.json();
console.log(json);
setFloats(json.deployments);
}
return (
<div>
<select id="deployment_selector" value={selectedFloats} className="selectpicker" data-live-search="true" multiple>
<option value="Float 1">Float 1</option>
{floats.map((float) => (
<option value={float.PLATFORM_NUMBER} key={float.PLATFORM_NUMBER}>
{float.LABEL}
</option>
))}
</select>
</div>
);
};
export default SelectFloats;

React.js: How to set default value in Select option?

I am trying to implement select-option in React using custom hooks and encountered an issue while trying to set a default value in select option. From the fetched data in UI, that comes from web API, I was able to show selected data based on category(in my case it's cuisine). But when I select default value to show All data, state doesn't update.
Another problem is about the duplicated values in select option. I need to have unique values as option values. I was thinking about to get unique values this way
<option key={restaurant.id}>{[...new Set(restaurant.cuisine)]}</option>
But this removes duplicated characters,but not the duplicated values.
Code below.
Hooks/useRestaurants component
import React, { useState, useEffect } from "react";
const useRestaurants = (cuisine) => {
const [allRestaurants, setAllRestaurants] = useState([]);
useEffect(() => {
fetch("https://redi-final-restaurants.herokuapp.com/restaurants")
.then((res) => res.json())
.then((result) => setAllRestaurants(result.results))
.catch((e) => console.log("error"));
}, []);
useEffect(() => {
if (cuisine === "All") {
const filterRestaurants = [...allRestaurants].filter((restaurant) => // here is my try
restaurant.cuisine.toLowerCase().includes(cuisine.toLowerCase())//code here doesn't work
);
setAllRestaurants(filterRestaurants);
} else {
const filterRestaurants = [...allRestaurants].filter((restaurant) =>
restaurant.cuisine.toLowerCase().includes(cuisine.toLowerCase())
);
setAllRestaurants(filterRestaurants);
}
}, [cuisine]);
return [allRestaurants];
};
export default useRestaurants;
App.js component
import React, { useState } from "react";
import useRestaurants from "./useRestaurants";
import Form from "./Form";
import Restaurant from "./Restaurant";
import "./styles.css";
export default function App() {
const [cuisine, setCuisine] = useState("All");
const [allRestaurants] = useRestaurants(cuisine);
const onChangeHandler = (e) => {
setCuisine(e.target.value);
};
return (
<div className="App">
<Form
onChangeHandler={onChangeHandler}
allRestaurants={allRestaurants}
cuisine={cuisine}
setCuisine={setCuisine}
/>
{allRestaurants &&
allRestaurants.map((restaurant) => (
<Restaurant restaurant={restaurant} key={restaurant.id} />
))}
</div>
);
}
And Form.js component
import React from "react";
const Form = ({ allRestaurants, cuisine, onChangeHandler }) => {
return (
<select onChange={onChangeHandler} value={cuisine}>
<option value={cuisine}>All</option>
{allRestaurants.map((restaurant) => (
<option key={restaurant.id}>{restaurant.cuisine}</option>
))}
</select>
);
};
export default Form;
Any help will be appreciated.
The useEffect in useRestaurants that is performing the filtering is missing allRestaurants from the dependency array. This means that the initial value (an empty array) will always be used within that useEffect. Thus, changing the cuisine will set allRestaurants to an empty array. However, you can't add allRestaurants to the dependency array and set it from within the effect. That will cause it to loop infinitely. The solution is to not use an effect - just create the filtered result and return it either as a separate value or in place of allRestaurants
// useRestaurants.js
import { useState, useMemo, useEffect } from "react";
const useRestaurants = (cuisine) => {
const [allRestaurants, setAllRestaurants] = useState([]);
useEffect(() => {
fetch("https://redi-final-restaurants.herokuapp.com/restaurants")
.then((res) => res.json())
.then((result) => setAllRestaurants(result.results))
.catch((e) => console.log("error"));
}, []);
const filteredRestaurants = useMemo(() => {
return cuisine === "All"
? allRestaurants
: allRestaurants.filter((restaurant) =>
restaurant.cuisine.toLowerCase().includes(cuisine.toLowerCase())
);
}, [cuisine, allRestaurants]);
return [allRestaurants, filteredRestaurants];
};
export default useRestaurants;
To fix the duplicate cuisine values you need to create the Set and then filter over that result. Your form is still filtering over all allRestaurants and {[...new Set(restaurant.cuisine)]} is just creating an array with a single value.
// Form.js
import React from "react";
const Form = ({ allRestaurants, cuisine, onChangeHandler }) => {
const cuisines = Array.from(new Set(allRestaurants.map((r) => r.cuisine)));
return (
<select onChange={onChangeHandler} value={cuisine}>
<option value='All'}>All</option>
{cuisines.map((cuisine) => (
<option id={cuisine}>{cuisine}</option>
))}
</select>
);
};
export default Form;
Remember to loop over the filtered restaurants in App.js
...
const [allRestaurants, filteredRestaurants] = useRestaurants(cuisine);
...
return (
...
{filteredRestaurants &&
filteredRestaurants.map((restaurant) => (
<Restaurant restaurant={restaurant} key={restaurant.id} />
))}
)

How to reset value of select box to default in react

I have the following code https://codesandbox.io/s/async-http-u9zsg
What I am trying to achieve is to reset the value of the second select box every time I switch movies.
I tried a bunch of different things without any luck. I'm not asking to write me the code, I just want a push in the right direction am I missing something, is my approach completely wrong?
thanks in advance
The code :
import React, { useState, useEffect } from "react";
import fetch from "node-fetch";
const query = `{
allFilms{
films{
title
characterConnection{
characters{
name
species{
name
}
homeworld{
name
}
}
}
}
}
}`;
function App() {
const [state, setState] = useState({
loading: true,
appliedFilters: {},
species: []
});
useEffect(() => {
const fetchMovieData = () => {
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query })
};
return fetch(
"https://swapi-graphql.netlify.com/.netlify/functions/index",
options
)
.then(res => res.json())
.then(res =>
setState({ ...state, data: res.data.allFilms.films, loading: false })
);
};
fetchMovieData();
}, []);
if (state.loading) {
return <div>Loading...</div>;
}
/*work from here */
const getSpeciesOptions = () => {
if (state.appliedFilters.movie === undefined) {
return <option>Select a movie first</option>;
}
const currentMovie = state.data.filter(
movie => movie.title === state.appliedFilters.movie
);
const characters = currentMovie[0].characterConnection.characters;
const speciesList = characters.map(char =>
char.species === null ? "unknown" : char.species.name
);
return [...new Set(speciesList)].map(specie => <option>{specie}</option>);
};
const handleFilterChange = e => {
setState({
...state,
appliedFilters: {
...state.appliedFilters,
[e.target.name]: e.target.value
}
});
};
console.log(state);
const removeFilters = () => setState({ ...state, appliedFilters: {} });
const movieOptions = () =>
state.data.map(movie => <option>{movie.title}</option>);
return (
<div className="App">
<form>
<select
name="movie"
onChange={e =>
e.target.value !== "default"
? handleFilterChange(e)
: removeFilters()
}
>
<option value="default">Please select movie</option>
{movieOptions()};
</select>
<select name="species">
<option value="default">Please select a species</option>
{getSpeciesOptions()}
</select>
</form>
</div>
);
}
export default App;
A better approach is to fully control the value of the second select as well. So we add the value of it to the state as well:
const [state, setState] = useState({
loading: true,
appliedFilters: {},
species: [],
selected: "default"
});
Update the second control to read the value from state and also manipulate the state on change:
<select
value={state.selected}
name="species"
onChange={e => {
setState({ ...state, selected: e.target.value });
}}
>
<option value="default">Please select a species</option>
{getSpeciesOptions()}
</select>
And finally also manipulate the callback of the first to also reset the value:
const handleFilterChange = e => {
setState({
...state,
appliedFilters: {
...state.appliedFilters,
[e.target.name]: e.target.value,
},
selected: "default"
});
};
https://codesandbox.io/s/hardcore-nash-vtyx7 working example
All you need to do is to create a ref for the second select control.
let ref = React.createRef();
...
<select ref={ref}name="species">
<option value="default">Please select a species</option>
{getSpeciesOptions()}
</select>
</form>
Then in the callback of the first select do this:
const handleFilterChange = e => {
setState(...);
ref.current.value = 'default';
};
With the ref to the elememt you can now manipulate the value.
Actually, you know ReactJS exactly, but your approach causes you are complicated now. For such cases just keep the state of second select and after changing the first select reset the second one.
<select name="species" onChange={handleSpeciesChanges}>
I mean to write a state handler for the second select and then in the state handler of first one just reset the second.

How to dispatch OnChange value ? e is not defined error React/redux

I'm building a filter component based on a movie list. I'm almost there. I've set up my action, reducer and components. Looking at my React/redux dev tools everything seems to be ok. however I can't dispatch my action properly.
Here is my component event :
<div>
Year:
<select
defaultValue={selectedYear}
onChange={e => onChangeYear(e.target.value)}
>
<option value="all" >All</option>
{years.map((y, i) =>
<option key={i} value={y}>{y}</option>
)}
</select>
</div>
and here is my dispatcher :
const mapDispachToProps = (dispatch) => {
return {
onChangeYear: () => {dispatch (onYearChange(e.target.value));
},
};
};
onYearChange is well imported and well defined in my action components.
how can I pass e.target.value in my dispatcher without any error ?
Thanks.
Change your select's onChange attribute to just be set to ={onChangeYear}, and in your dispatcher accept e as a parameter.
const mapDispachToProps = (dispatch) => {
return {
onChangeYear: () => {dispatch (onYearChange(e.target.value));
},
};
becomes
const mapDispachToProps = (dispatch) => {
return {
onChangeYear: (e) => {dispatch (onYearChange(e.target.value));
},
};

Resources