Nov 30, 2021 04:01 AM
Any help on this issue would be really appreciated!
I’m making a simple app to create quotes. I’ve got everything up and running to edit fields with the <Input />
component and for anything other than text it works great e.g.
function updateRecord (quote, recordFields) {
quotesTable.updateRecordAsync(quote, recordFields)
}
<Input
name="Hire Start"
type="date"
value={quote.getCellValue('Hire Start')}
onChange={(e) => updateRecord(quote, {'Hire Start': e.target.value})}
/>
However, when the input type is text (or currency, anything that can be typed quickly), there’s a very noticeable delay in the update to the cell value which makes input glitchy and, if you type fast enough, inputs get dropped.
I then switched to controlling the <Input />
with a state object, only writing back to Airtable in a useEffect()
that watched the state object. This solved the issue but now updates made in Airtable (outside of the app) are of course not reflected in the app until next render.
How do I find a middle-ground between these two patterns? Need to be able to input smoothly but have data go back and forth between Airtable and the app in realtime?
Hope this makes sense to someone but can share more of my code if needed.
Thanks!
Solved! Go to Solution.
Dec 07, 2021 05:31 AM
Hi Jack
interesting approach, so you’re updating the record when you lose focus from the input.
I’ll just paste my approach below for the record. The strange thing is that I don’t seem to need to use useEffect to update the defaultValue for the text box…
Steve
function updateRecord (quote, recordFields) {
quotesTable.updateRecordAsync(quote, recordFields)
}
function MyInput({quote){
const [isEditing, setIsEditing] = useState(false)
if(!isEditing){
return(
<Input
name="Hire Start"
type="date"
value={quote.getCellValue('Hire Start')}
onFocus={() => setIsEditing(true)}
/>
)
}else{
return (
<Input
type="date"
defaultValue={quote.getCellValue('Hire Start')}
onChange={(e) => updateRecord(quote, {'Hire Start': e.target.value})}
onBlur={() => setIsEditing(false)}
/>
)
}
}
Nov 30, 2021 10:29 AM
What about watching the field for that record with useRecordById
? When the cell value changes, compare it with the current and/or previous versions of the state object storing the user input? If it is different (due to an outside change), update the state object.
Unrelated, is there a reason why are you writing back to Airtable with useEffect()
instead of directly in the onChange
event?
Dec 01, 2021 12:54 AM
Thanks for your response! That’s an interesting idea with the useRecordById
, I must admit I hadn’t read the API docs closely enough so didn’t realise it behaves differently to useRecords()
but will give it a try.
As for useEffect()
, I don’t know why I thought I needed to do that? Something about avoiding a loop with updating the record and the state object both triggering re-renders? Also, using the dependencies array to watch for changes to state? I’m don’t know really?
Dec 03, 2021 02:12 PM
I had this problem too. Solved it by having 2 inputs, one controlled and the other uncontrolled. I used the onfocus event of the controlled input to hide the controlled input, and show the uncontrolled input. Thus the record is updated using the onchange event of the uncontrolled input, and when finsihed, use the onblur event of the uncontrolled input to re show the controlled input, which has automatically been updated to the new field value. It’s actually quite simple to do although it sounds a bit complicated.
ps - using a useState to control the visibility of the inputs.
Dec 06, 2021 10:02 AM
Thanks, @Steve_Haysom, appreciate the advice. Will give this a go.
Dec 07, 2021 02:27 AM
Here’s the relevant parts of what I ended up with. Not sure if there is some redundancy in here but it works for typing fast and updating from either the app or Airtable’s interface (unless two people are editing the same field at the same time but that is not something I feel I need to solve for). Open to any feedback if I’ve done something bone-headed!
// State for the value I'm editing
const [notes, setNotes] = useState({
value: quote.getCellValue("Notes"),
editing: false,
});
// Update Airtable function
function updateRecord(quote, recordFields) {
quotesTable.updateRecordAsync(quote, recordFields);
}
<textarea
name="Hire Notes"
style={{
width: "100%",
backgroundColor: "#F2F2F2",
border: "none",
borderRadius: "5px",
padding: "8px"
}}
rows={3}
// If editing use state value, else use cell value
value={
notes.editing === true
? notes.value
: quote.getCellValue('Notes')
}
// Select the input and switch to editing
onFocus={() =>
setNotes({
value: quote.getCellValue('Notes'),
editing: true}
)}
// Finish editing and update record, switch to not editing
onBlur={(e) => {
updateRecord(quote, {'Notes': e.target.value})
setNotes({value: e.target.value, editing: false})
}}
// Update state while editing
onChange={(e) =>
setNotes({...notes, value: e.target.value})}
/>
Dec 07, 2021 05:31 AM
Hi Jack
interesting approach, so you’re updating the record when you lose focus from the input.
I’ll just paste my approach below for the record. The strange thing is that I don’t seem to need to use useEffect to update the defaultValue for the text box…
Steve
function updateRecord (quote, recordFields) {
quotesTable.updateRecordAsync(quote, recordFields)
}
function MyInput({quote){
const [isEditing, setIsEditing] = useState(false)
if(!isEditing){
return(
<Input
name="Hire Start"
type="date"
value={quote.getCellValue('Hire Start')}
onFocus={() => setIsEditing(true)}
/>
)
}else{
return (
<Input
type="date"
defaultValue={quote.getCellValue('Hire Start')}
onChange={(e) => updateRecord(quote, {'Hire Start': e.target.value})}
onBlur={() => setIsEditing(false)}
/>
)
}
}
Dec 07, 2021 06:11 AM
That’s interesting. It hadn’t occurred to me that I could just leave out the value
parameter on the second input. Is there any noticeable effect on swapping out the input? Like do you have to refocus?
Dec 07, 2021 06:31 AM
There’s no noticeable effect at all, you can click in the middle of a word and it will put the cursor exactly where you clicked even though it’s replacing one control with another. Another mystery!
It also makes it useful to have 2 separate controls, as you can format the display (controlled) input completely differently to the editing input (eg if you are working with currencies, decimal numbers etc)
ps the value parameter is missing from the second input because it’s an uncontrolled input, ie the value is not locked by react.
Dec 07, 2021 06:34 AM
Very nice. Just gave it a go. Thanks a bunch for your help.