I'm using Inertia.js and I've been loving it.
I'm having a problem anyway that I cant seem to figure out. I have a select input that will contain options from the database but a lot of them aprox. 30/40k so my idea is to just take the first 20 and then let the user input a search and bring the first 20 that match that result.
All my backend is working ok but the problem I'm having is displaying the updated select options after search.
The relevant code is the following: First the call to AsyncSelect Component from the react-select package. This components needs a loadOptions property that will call a function when the user start typing
<AsyncSelect
name="proveedores"
id="proveedores"
defaultOptions={proveedores}
loadOptions={handleChange}
/>
The function that gets call is this, it recieves the input value and a callback, within this callback I'm supposed to search for the new data and return it
function handleChange(inputValue,callback) {
setValues(values => ({
...values,
["search"]: inputValue
}));
callback()
}
The problem is that to load the new data on search I'm using useEffect
useEffect(() => {
if (prevValues) {
const query = pickBy(values)
Inertia.get(window.location.href, query, {
replace: true,
preserveState: true
});
}
}, [values]);
The search with useEffect works ok, if I do a console.log I will see the list of proveedores updated but the problem is how do I tell the callback from handleChange function that it should listen for useEffect and on finish use the new value of proveedores
If anyone had to do something like this (React Select with search data from database) using Inertia + React, i would really appreciate some feedback on how to achieve it.
Thanks!!
Related
I am making a search functionality into react that effectively looks for data from json-server for a match. I don't want to provide a debounced search to the input field, rather I want to trigger the search when "Enter" key is pressed. So i used onKeyPress prop from MUI's textfield, where I provided the logic to send the query to the server.
Please acknowledge my code as mentioned below -
imports...
export default function AppSearchBar ( ) {
// local state for searchTerm
const [ searchTerm, setSearchTerm ] = useState<string>('');
// using redux - action
const {loadedBooks} = useAppSelector(state => state.books);
const {loadedGames} = useAppSelector(state => state.games);
// these 'loadedBooks' and 'loadedGames' are of type boolean and are initially false (coming from my slices) and set to true when their requests are getting fulfilled.
const dispatch = useAppDispatch();
// useCallback
const fetchAllCategories = useCallback(() => {
setTimeout( async () => {
await dispatch(fetchBooksAsync( searchTerm )); // this is async thunks i created to fetch books into my bookSlice.ts file
await dispatch(fetchGamesAsync( searchTerm )); // this is async thunks i created to fetch books into my gameSlice.ts file
}, 500);
}, [ searchTerm , dispatch ]);
// effect when searchTerm mounts
/* useEffect(() => {
fetchAllCategories()
}, [ fetchAllCategories ]); */ // dependency as the function itself.
// I want this useEffect, but un-commenting this is not allowing my "handleSearchOnEnter" to come into the picture at all, but, I want that first load of all cars be automatic, and then when I write something to input, on pressing enter it should search, and finally when I wipe input field, it should return me back all the cards.
const handleSearchOnEnter = ( event : any ) => {
if ( event.key === "Enter" ) {
fetchAllCategories(); // this is wrapped inside of useCallBack and effect is produced using useEffect.
}}
return (
<Fragment>
<TextField
value = {searchTerm}
onChange = {( event : any ) => setSearchTerm(event.target.value)}
onKeyPress = { searchTerm !== "" ? handleSearchOnEnter : undefined } />
</Fragment>
)
}
Now, problem statement -
Whenever I load my window, all Books and Games are not loaded at all (if I remove useEffect() from code). They only loads when I press enter. But, I don't want this behaviour.
If I keep useEffect() - hook, then they behaves like debounce search and onChange of my text input field, they return the searched result.
What I want is as follows -
- Whenever I first loads the window, all products get loaded.
- Whenever I write something into the input field, then it shouldn't call (fetchFiltersAsync() - which is the API call for full-text search on Json-Server) until i press Enter key, only When I press enter, it should call the API and fetch the relevant results.
- After that, when I manually remove the searchedItem from input field (wiping it), all of my data should get returned. (i.e display all cards into UI)
What is Happening? -
Whenever My window loads, all of my data/cards are not getting loaded., until I presses enter key...cool
When I type something into input field, it fetches the searched results without even taking "Enter" (because of open useEffect() into the code)
When I remove a term from the input field, my data is not getting loaded automatically back to like as they were into first visit (all cards visible).
All controls are here only (into the code), I have to do something with searchTerm, so whenever searchTerm is not empty, then my handleSearchOnEnter() function should get called.
I mean, it should produce useEffect automatically, but only when a searchTerm is not being provided. If the searchTerm is being provided, then it should trigger handleOnEnterSearch()
I had the same issue that is described in the second Problem I solved it by adding in my project.
<form onSubmit={onKeyDownHandler}>
<TextField>{"Some Code"}</TextField>
</form>;
Also you can create an useState and giving it new Date will refresh your table better.
const onKeyDownHandler = (e) => {
e.preventDefault();
// if (searchTxt.length >= 3 || !searchTxt.length) {
dispatch(setSearchTxt(searchTxtTemp));
setTriggerSearch(new Date().getTime());
// }
};
But the bad sides of this code is when you remove everything from input you need to press enter again to refresh.
I am currently working on a fun project and I want to know how to do something that has stumped for a bit.
Basically I am using Axios to get my data and then rendering data out in a .map func then I have a click function to show only the data that is corresponding to the ID for example ID 1 has some values that I want to show in another component. How do I do that?
https://j99t7.csb.app/
If you see my sand box and click on one of the ids and see the console / code - this is where I am stuck at.
Cheers,
Dave :)
In order to filter the data, you can use something like:
const [filteredData, setFilteredData] = useState([]);
//onclick
setFilteredData(data.filter(element => element.id === id));
//jsx return
filteredData.map(filteredElement => {
//loop through elements and display data desired
})
Though I'm not a React Master, onClick doesn't return JSX or TSX.
i.e where would the returned value be rendered, in most cases, it used as a void function with no return value
I'm trying to build a component that retrieves a full list of users from Amazon AWS/Amplify, and displays said results in a table via a map function. All good so far.
However, for the 4th column, I need to call a second function to check if the user is part of any groups. I've tested the function as a button/onClick event - and it works (console.logging the output). But calling it directly when rendering the table data doesn't return anything.
Here is what I've included in my return statement (within the map function)
<td>={getUserGroups(user.email)}</td>
Which then calls this function:
const getUserGroups = async (user) => {
const userGroup = await cognitoIdentityServiceProvider.adminListGroupsForUser(
{
UserPoolId: '**Removed**',
Username: user,
},
(err, data) => {
if (!data.Groups.length) {
return 'No';
} else {
return 'Yes';
}
}
);
};
Can anyone advise? Many thanks in advance if so!
Because you should never do that! Check this React doc for better understanding of how and where you should make AJAX calls.
There are multiple ways, how you can solve your issue. For instance, add user groups (or whatever you need to get from the backend) as a state, and then call the backend and then update that state with a response and then React will re-render your component accordingly.
Example with hooks, but it's just to explain the idea:
const [groups, setGroups] = useState(null); // here you will keep what "await cognitoIdentityServiceProvider.adminListGroupsForUser()" returns
useEffect(() => {}, [
// here you will call the backend and when you have the response
// you set it as a state for this component
setGroups(/* data from response */);
]);
And your component (column, whatever) should use groups:
<td>{/* here you will do whatever you need to do with groups */}</td>
For class components you will use lifecycle methods to achieve this (it's all in the documentation - link above).
I got simple blog with arficles, and when user click edit button he got form filled with articles data - title, description, body and tags. I use useEffect to get data and fill form, when I got "id". If there is no "id" form should be blank. here is my useEffect:
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, [id]
);
but when I reload page id not changed, and func userService.articles.get(id) not run, and all datas gone. I need advice how to fix it? may be I need to use other deps for useEffect, but now I have no idea what deps i can use exept id.
upd:
thank you all for help. all i want is:
when the edit page load/reload and "id" exist -> fills form fields
when "id" not exist -> blank form fields
now when I reload edit page i got id - but all datas gone, and i got blank form :(
Here is the full code: codesandbox
p.s. i use free API - so you can create user in a sec with any imagined email, username and password. you don't need mail confirmation.
You should use this.props.match.params to access your id that comes from the url.
useEffect(() => {
if (props.match.params.id) {
setIsloading(true);
userService.articles.get(props.match.params.id)
.then((resp) => {
setIsloading(false);
props.onLoad(resp)
})
} else {
props.onLoad(null);
}
}, [props.match.params.id]);
Also you should rely on useState to manage your isLoading variable.
const [isLoading, setIsloading] = useState(false);
I did a bit more digging into the code you have provided.
The initialValues will be first empty because the data coming from the props is not there yet. And once the initialValues have been set you can't change them dynamically, you have to resort to the antd Form api.
You cannot set value for each form control via value or defaultValue
prop, you should set default value with initialValues of Form. Note that initialValues cannot be updated by setState dynamically, you
should use setFieldsValue in that situation.
The key here is to use another useEffect with dependencies to your form values comming from the props and use those to reset the form values via setFieldsValue.
try to useEffect without options and it will run just when the page loads for the first time
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, []
);
Based on the assumption that you want props.onLoad to run whenever there is a defined "id" or the defined "id" changes:
Returning a function from a useEffect hook (as you do with return props.onLoad(...)) is specifically for "cleaning up" things like side effects or subscriptions. A function returned inside a useEffect hook will only run when the component unmounts. See docs here. Also it doesn't seem like you are even passing a function to run on cleanup. You're passing the result of props.onLoad to run on cleanup, which based on the function name doesn't seem like it is intended to return another function.
So, if you want props.onLoad() to run if the "id" is defined, remove the return before props.onLoad. That return is telling React to hold (what it thinks is a function) for cleanup on unmount. If it's still not working, I think we'll need more information on what exactly props.onLoad is doing.
I am trying to implement a table in react where the user can edit individual rows by clicking the edit button on a row and then submit once he has made his change. I have say two components App.js and its child Table.js to implement this.
The way I thought of doing this initially was letting each of this component have their own state for rows and then the Table component reads from the props send to it by parent initially and only change the parent rows when users submits the change as oppose to onChange event. But I've read that reading props into state is an anti-pattern.
So decided to have everything in the parent by having two values for row (oldrows,newrows). And using them to maintain state instead, This is the design I came up with :
But what happens is whenever I click cancel the oldRows get bound to the newRows, here is a codePen example I put up:
https://codepen.io/snedden-gonsalves/pen/zYOVMWz
handleChangeRowInput = (event, keyValue) => {
let keyVals = [...this.state.newValuesArray];
keyVals[this.state.editIndex][keyValue] = event.currentTarget.value;
this.setState({
newValuesArray: keyVals
})
}
handleCancelRowInput = () => {
this.setState({
newValuesArray: [...this.state.oldValuesArray],
editIndex: -1
})
console.log('array', this.state.newValuesArray)
}
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: [...this.state.newValuesArray],
editIndex: -1
})
}
In the codePen example if you enter a new value then cancel and then try adding a new value again the the old values and new values get bound.
I tried using lodash deepClone but it didn't work out, not sure why this is happening.
Also if you could comment on what is the best way to design this in react that would be awesome as I am very new to react and just trying to learn ..
I didn't find any issue after the cancel function. For me, the issue was coming up after I called the save function.
After clicking on the save button and then editing again, the old values and new values were get bound.
The handleSubmitRowInput function should create a new array for the oldValuesArray using the cloneDeep function
handleSubmitRowInput = () => {
this.setState({
oldValuesArray: _.cloneDeep(this.state.newValuesArray),
editIndex: -1
})
}