React-select Async loadOptions with Rest-api - reactjs

How to pass data to another select after clicking on the first one. I have a select of countries, when clicking on them, regions are loaded into the second select loadOptions.
import dummy from './dummy';
import AsyncSelect from "react-select/async";
//This is country LoadOptions
const loadOptions = (inputValue, callback) => {
dummy.get(`country?CountrySearch[query]=${inputValue}`)
.then((response) => {
const options = []
response.data.data.forEach((permission) => {
options.push({
label: permission.name,
value: permission.id
})
})
callback(options);
})
}
Hooks with options Array(regions)
const [a, b] = useState([''])
let loadOptions2 = async (a) => {
return a
};
After onChange country select, get new data for regions
const handleCountry = (value) =>{
dummy.get(`region?filter[country_id]=${value.value}`)
.then((response) => {
const options = [];
response.data.data.forEach((permission) => {
options.push({
label: permission.name,
value: permission.country_id
})
})
b(options)
loadOptions2 = () => {
return options
}
console.log(a)
return options
})
}
Country Select
<AsyncSelect
cacheOptions
defaultOptions
onChange={handleCountry}
loadOptions={loadOptions}
/>
Region Select
<AsyncSelect
cacheOptions
defaultOptions
value={selectedCity}
onChange={handleCity}
loadOptions={loadOptions2}
/>
Console.log is show regions array, but loadOptions is empty

Related

Updating displayed data on mouse enter

I would like to update text which is displayed inside a <div> element. I would love to do it when the cursor enters the <div> element.
Basically I'm fetching some data from the API and I display only one parameter (name). If a user enters the <div> with the name displayed I would like to show some details, i.e. description and price.
This is my code which I tried to complete my task.
import {useEffect, useState} from "react";
import requestOptionsGet from "../utilities/requestOptions";
import validateResponse from "../utilities/validators";
const Warehouse = () => {
const [items, setItems] = useState([]);
const [texts, setTexts] = useState([]);
const getItems = async () => {
const url = "http://localhost:8000/api/items/"
return await fetch(url, requestOptionsGet)
.then((response) => validateResponse(response, url))
.then((response) => response.json())
.then((data) => setItems(data))
};
useEffect(() => {
getItems();
}, []);
useEffect(() => {
setTexts(items.map((item) => (
{
id: item.id,
name: item.name,
description: item.description,
price: item.price,
currentDisplay: <h2>{item.name}</h2>,
})
))
}, [items]);
const displayName = (data) => {
console.log(
"displayName"
);
};
const displayDetails = (data) => {
const itemID = parseInt(data.currentTarget.getAttribute("data-item"));
const displayInfo = texts.find(text => text.id === itemID);
displayInfo.currentDisplay = <p>{displayInfo.description}</p>
setTexts(texts);
console.log(texts);
console.log(
"displayDetails"
);
return(
displayInfo.currentDisplay
);
};
return(
<div className="container">
<h1>Your warehouse.</h1>
<h2>All your items are listed here.</h2>
<hr />
{texts.map((text) => (
<button className="container-for-single-item" id={text.id} key={text.id}
onMouseEnter={displayDetails} onMouseLeave={displayName} data-item={text.id}>
{text.currentDisplay}
</button>
))}
</div>
);
}
export default Warehouse;
The functions work (everything is displayed in the console as it should be) and even the texts change. However the paragraph does not appear. How can I fix my code? Thanks!
Never modify state directly
const newTexts = texts.map(text => text.id === itemID ? { ...text, currentDisplay: <p>{text.description}</p> } : text);
setTexts(newTexts);

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?

How to set the first option of the select as default value

I would like to set the selectedSubscriber value with first item from subscriberOptions array (subscriberOptions[0].value). What is the best way to do it?
const defaultFormInput = {
subscriberOptions: [],
selectedSubscriber: "",
subscriberOptionsIsLoading: true,
};
const [formInput, setFormInput] = useState(defaultFormInput);
useEffect(() => {
//load subscribers from API
async function loadSubscribers() {
const response = await fetch("https://myapi/subscribers");
const body = await response.json();
setFormInput({
...formInput,
subscriberOptions: body.map((x) => ({ value: x.id, name: x.name })),
subscriberOptionsIsLoading: false,
});
// not working
//setFormInput({
// ...formInput,
// selectedSubscriber: formInput.subscriberOptions[0].value,
//});
}
loadSubscribers();
}, []);
JSX
<Select
disabled={formInput.subscriberOptionsIsLoading}
value={formInput.selectedSubscriber}
name="selectedSubscriber"
onChange={handleChange}
>
{formInput.subscriberOptions &&
formInput.subscriberOptions.map((item) => {
return (
<MenuItem key={item.value} value={item.name}>
{item.name}
</MenuItem>
);
})}
</Select>
you need to map your data and set at the same state, it should work.
const defaultFormInput = {
subscriberOptions: [],
selectedSubscriber: "",
subscriberOptionsIsLoading: true,
};
const [formInput, setFormInput] = useState(defaultFormInput);
useEffect(() => {
//load subscribers from API
async function loadSubscribers() {
const response = await fetch("https://myapi/subscribers");
const body = await response.json();
const mappedData = (body || []).map((x) => ({ value: x.id, name: x.name }));
const [defaultSelect] = mappedData || [];
setFormInput({
...formInput,
subscriberOptions: mappedData,
selectedSubscriber: defaultSelect.value,
subscriberOptionsIsLoading: false,
});
}
loadSubscribers();
}, []);

React Select Async creatable doesn't save to the state

I am using react select async creatable,
the data from api loads correctly and can select it also can create new values
but after selecting a choice or if not, creating a new value, seems like my titleState becomes empty, I tried consoling the state but gives me blank/empty value on console
const [titleState,setTitleState] = useState('')
const loadOptions = (inputValue, callback) => {
axios.post(`sample/api`, {
data: inputValue
})
.then(res => {
callback(res.data.map(i => ({
label: i.title,
value: i.title,
})))
console.log(res)
})
.catch(err => console.log(err))
}
const handleInputChange = (newValue) => {
setTitleState(newValue)
}
const logState = () => {
console.log(titleState) // logs empty in the devtools
}
<AsyncCreatableSelect
menuPortalTarget={document.body}
styles={{ menuPortal: base => ({ ...base, zIndex: 9999 }) }}
loadOptions={loadOptions}
onInputChange={handleInputChange}
placeholder="Title Trainings"
/>
<Button onClick={logState}>Click</Button>
then I have some button when click will console the titleState, but it doesn't log it.
You should use onChange={handleInputChange} instead of onInputChange={handleInputChange}
onChange triggers when a new option is selected / created.
onInputChange triggers when writing a new option.

Set value to textfield with hooks and redux material ui

I'm building an app using react, redux, and redux-saga.
The situation is that I'm getting information from an API. In this case, I'm getting the information about a movie, and I will update this information using a basic form.
What I would like to have in my text fields is the value from the object of the movie that I'm calling form the DB.
This is a brief part of my code:
Im using 'name' as an example.
Parent component:
const MovieForm = (props) => {
const {
movie,
} = props;
const [name, setName] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({
name,
});
};
const handleSetValues = () => {
console.log('hi');
console.log(movie, name);
setName(movie.name);
setValues(true);
};
useEffect(() => {
if (movie && values === false) {
handleSetValues();
}
});
return (
<Container>
<TextField
required
**defaultValue={() => {
console.log(movie, name);
return movie ? movie.name : name;
}}**
label='Movie Title'
onChange={(e) => setName(e.target.value)}
/>
</Container>
);
};
export default MovieForm;
....
child component
const MovieUpdate = (props) => {
const { history } = props;
const { id } = props.match.params;
const dispatch = useDispatch();
const loading = useSelector((state) => _.get(state, 'MovieUpdate.loading'));
const created = useSelector((state) => _.get(state, 'MovieUpdate.created'));
const loadingFetch = useSelector((state) =>
_.get(state, 'MovieById.loading')
);
const movie = useSelector((state) => _.get(state, 'MovieById.results'));
useEffect(() => {
if (loading === false && created === true) {
dispatch({
type: MOVIE_UPDATE_RESET,
});
}
if (loadingFetch === false && movie === null) {
dispatch({
type: MOVIE_GET_BY_ID_STARTED,
payload: id,
});
}
});
const updateMovie = (_movie) => {
const _id = id;
const obj = {
id: _id,
name: _movie.name,
}
console.log(obj);
dispatch({
type: MOVIE_UPDATE_STARTED,
payload: obj,
});
};
return (
<div>
<MovieForm
title='Update a movie'
buttonTitle='update'
movie={movie}
onCancel={() => history.push('/app/movies/list')}
onSubmit={updateMovie}
/>
</div>
);
};
export default MovieUpdate;
Then, the actual problem is that when I use the default prop on the text field the information appears without any problem, but if i use defaultValue it is empty.
Ok, I kind of got the answer, I read somewhere that the defaultValue can't be used int the rendering.
So I cheat in a way, I set the properties multiline and row={1} (according material-ui documentation) and I was able to edit this field an receive a value to display it in the textfield

Resources