FilePond shows as "untouched" in react-hook-form and the "required" validation is always triggered - react-admin

I am using react-hook-form (in react-admin) and I created a custom input for FilePond.
While everything works fine (files are uploaded, etc.), the validation fails because the form considers the field as "untouched" (and it shows it always as "invalid", failing the "required" validation).
How can we force a FilePond Input to show as "touched" in react-hook-form?
See image below:
And this is the container code that creates the input (the FilePond component is a child of that)
const AttachmentsUploadInput = ({ source, validationMessage, ...props }) => {
const input = useController({ name: source });
// 'input' has {field, fieldState, formState}
// console.log('input', input);
// See props of {...input} here:
// https://react-hook-form.com/api/usecontroller
const notify = useNotify();
return <AttachmentsUpload {...input} notify={notify} {...props} />;
};
How can we solve this?

Related

Problem passing animatedPosition from BottomSheet to child to animate image

I cant seem to pass the onchange Y value from #gorhom/bottom-sheet. I can see the value when onchange in my App.js but I need to pass it another external component.
I am not sure if this involves using useDerivedValue or useEffect. I manage to pass the initial value from a state but even that is one behind.
I have an App.js that has the bottomSheet in it and another external .js file that loads images that are passed from the App.js, so everything is passing ok. Do I need to use useEffect to listen for changes?
As it stands I am using this in my App
const bottomSheetRef = useRef < BottomSheet > null;
// variables
const snapPoints = useMemo(() => [200, 538], []);
// callbacks
const handleSheetChanges = useCallback((index: number) => {
console.log("handleSheetChanges", index);
}, []);
const animatedPosition = useSharedValue(0);
const [currentPosition, setCurrentPosition] = React.useState(0);
useDerivedValue(() => {
console.log(
animatedPosition.value,
"here you will get value of every position"
);
}, [animatedPosition]);
useEffect(() => {
console.log(currentPosition);
}, [currentPosition]);
And how I am using it,
<BottomSheet
useRef={bottomSheetRef}
index={1}
snapPoints={snapPoints}
// onChange={handleSheetChanges}
animateOnMount={false}
animatedPosition={animatedPosition}
onChange={(index) => setCurrentPosition(index)}
>
This all works great but when I try to pass it, it does not update, I am using this code in my exertnal file,
export default function BottomSheetData(props) {
const {
show,
imga,
imgb,
imgc,
selectedItem,
imgWidthV,
imgHeightV,
animatedPosition,
} = props;
which gets all the relevant data just not the "live" changes?
I have tried everything but its now causing me a headache!!! I hope I have explained this issue properly and provided enough code. All I need it being pointed in the right direction or being told I am going around this in the totally wrong way.

ReactNative UI freezing for a second before rendering a component with a fetch in useEffect()

TL;DR: My UI freezes for .5-1s when I try to render a component that does a API fetch within a useEffect().
I have ComponentX which is a component that fetches data from an API in a useEffect() via a redux dispatch. I'm using RTK to build my redux store.
function ComponentX() {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(fetchListData()); // fetch list data is a redux thunk.
}, [dispatch]);
...
return <FlatList data={data} /> // pseudo code
}
as you can see the fetch will happen everytime the component is rendered.
Now I have ComponentX in App along with another component called ComponentY.
Here's a rudamentary implementation on how my app determines which component to show. Pretend each component has a button that executes the onClick
function App() {
const [componentToRender, setComponentToRender] = useState("x");
if (componentToRender === "x") {
return <ComponentX onClick={() => setComponentToRender("y")}/>
} else {
return <ComponentY onClick={() => setComponentToRender("x")}/>
}
}
Now the issue happens when I try to move from ComponentY to ComponentX. When I click the "back" button on ComponentY the UI will freeze for .5-1s then show ComponentX. Removing the dispatch(fetchListData()); from the useEffect fixes the issue but obviously I can't do that since I need the data from the API.
Another fascinating thing is that I tried wrapping the dispatch in an if statement assuming that it would prevent a data fetch thus resolving the "lag" when shouldReload is false. The UI still froze before rendering ComponentX.
useEffect(() => {
if (shouldReload) { // assume this is false
console.log("reloading");
dispatch(fetchListData());
}
}, [dispatch, shouldReload]);
Any idea what's going on here?
EDIT:
I've done a little more pruning of code trying to simplify things. What I found that removing redux from the equation fixes the issue. By simply doing below, the lag disappears. This leads me to believe it has something to do with Redux/RTK.
const [listData, setListData] = useState([]);
useEffect(() => {
getListData().then(setListData)
}, []);
Sometimes running the code after interactions/animations completed solves the issue.
useEffect(() => {
InteractionManager.runAfterInteractions(() => {
dispatch(fetchListData());
});
}, [dispatch]);

Set form values with mobx-react-form using object/props

I'm using mobx-react-form and I need to fill a form with default values pulled from an object in my store. Unfortunately, if I try to use FormModel.$("email").set(object.email); inside my component mobx complains that I can't modify observed objects outside of an action and I exceed maxdepth.
Specifically my code looks like this (some details removed for clarity)
import React from 'react';
import ReactDOM from "react-dom"
import { observer } from "mobx-react-lite"
import validatorjs from 'validatorjs';
import MobxReactForm from 'mobx-react-form';
const fields = [{
name: 'email',
label: 'Email',
placeholder: 'Email',
rules: 'required|email|string|between:5,25',
// value: user.email,
}, …
]
const FormModel = new MobxReactForm({ fields }, { plugins, hooks }); //nothing exception here standard plugins/hooks
const UserForm = observer(({open, onClose, object}) => { //My component…object has fields with names email…
FormModel.$("email").set(object.email); //This works fine if I replace object.email with "foo"
return (<MobxInput field={FormModel.$("email")} fullWidth />);
});
export default UserForm;
Yes, I've checked the object has the appropriate fields (it's just a bare object passed in from parent …not even an observable object in this case).
My first approach was to simply put everything inside UserForm and simply fill the values in fields from object but when I do this typing doesn't work in the resulting form (I suspect that mobx is trying to observe an object created inside that observer and that doesn't work).
The problem is I need to use the same form sometimes with data suppled by a user object from my user store and sometimes with blank values to create a new user and I'm kinda stuck about how to do this now.
First of all, you can't do that:
const UserForm = observer(({open, onClose, object}) => {
// This won't really work very well
FormModel.$("email").set(object.email);
return (<MobxInput field={FormModel.$("email")} fullWidth />);
});
Because every time you change value in your input your whole UserForm component also rerenders (because it observes FormModel.$("email") value which just changed) and when it rerenders you instantly change new value to your old value from object. I am not sure why exactly you getting maxdepth error, but there might even be endless loop here as you can see in some cases. Modifying anything like that inside render is usually a bad practice. You need to use useEffect at least, or something like that.
I can't modify observed objects outside of an action
This happens because you need to do all mutations inside actions by default. You can configure it though, if you don't like it:
import { configure } from "mobx"
configure({
enforceActions: "never",
})
But it is better to stick with it, it might catch some unwanted behaviour.
I've made quick Codesandbox example with some of your code, it shows how you can make several forms and pass default values to them:
const UserForm = observer(({ object }) => {
const [FormModel] = useState(() => {
const fields = [
{
name: 'email',
label: 'Email',
placeholder: 'Email',
rules: 'required|email|string|between:5,25',
value: object?.email || ''
}
];
return new MobxReactForm({ fields }, { plugins });
});
return (
<form onSubmit={FormModel.onSubmit}>
<input {...FormModel.$('email').bind()} />
<p style={{ color: 'red' }}>{FormModel.$('email').error}</p>
<button type="submit">submit</button>
</form>
);
});
That is just one of many ways, it all depends what you need in the end.

Validation form with optional fields react-hook-forms

I'm trying to handle a form composed by two parts, one fixed and the other one that gets displayed by a switch.
To handle the forms I'm using react-hook-form.
I defined a validation scheme in the file validation.ts inside the constants folder.
About the optional part I defined a sub-object but it doesn't work and it gives a compile time error.
Because of this I opted for the solution you'll find in the link at the bottom of the page
Although I defined the optional input fields inside the validation file, they don't get recognized when I press the submit button.
How can I fix this problem?
At this link you can find a working example of the problem.
The main problem is with your components/Form component, it has this line
// components/Form.tsx
return child.props.name
? .... : child;
What you have done here is ignoring all child components without the name prop,
where as when rendering the component what you did was rendered them inside <></>, use below alternative instead.
// in App.tsx
{isEnabled ? <Input name="trailerPlate" placeholder="Targa rimorchio" /> : <></>}
{isEnabled ? <Input name="trailerEnrolment" placeholder="Immatricolazione rimorchio" /> : <></>}
Still the validation won't because you need to register the components and your current useEffect code doesn't account for change in number of input fields.
Use below code instead
React.useEffect(() => {
....
....
}, [
register,
Array.isArray(children) ?
children.filter(child => child.props.name).length : 0
]
);
We are using the count of child components with name prop as a trigger for useEffect.
And finally you also have to unregister the fields when you toggle the switch,
below is a sample code, feel free to change it according to your preference.
const { handleSubmit, register, setValue, errors, unregister, clearErrors } = useForm<VehicleForm>();
const toggleSwitch = () => {
if (isEnabled) {
unregister('trailerEnrolment');
unregister('trailerPlate');
}
clearErrors();
setIsEnabled(prev => !prev)
};
Feel free to upvote if I was helpful.

Spying on React components using Enzyme (and sinon?) to check arguments

I'm wanting to assert that a component gets called from within another component with the correct arguments.
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
I'm pretty sure I want to use a sinon spy and do something like this
const titleSpy = sinon.spy(Title, render)
expect(titleSpy).to.be.calledWith( '< some title >' )
but with regards to React and Enzyme, I'm not really sure what I should be spying on. (Because apparently it's not render!)
In my spec file I am importing Title and console.loging it's value to find a function to spy on and I get:
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, Object.getPrototypeOf(_class).apply(this, arguments));
}
Any ideas on how I can do this? Is it a case of going through and finding the element and checking it's attributes? If so that seems a bit...messy and seems like it goes against the principle of the Shallow render ("Shallow rendering is useful to constrain yourself to testing a component as a unit").
If you're just checking the value of properties passed to the component, you don't need sinon. For example, given the following component:
export default class MyComponent extends React.Component {
render() {
return (
<MyComponent myProp={this.props.myProp} />)
}
}
Your test might look like this:
describe('MyComponent ->', () => {
const props = {
myProp: 'myProp'
}
it('should set myProp from props', () => {
const component = shallow(<MyComponent {...props} />)
expect(component.props().myProp).to.equal(props.myProp)
})
})
You can achieve it with the help of .contains() method, without messing up with spies.
If you have a component:
<Foo>
<Title title="A title" url="http://google.com" />
</Foo>
You can make such an assertion:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A title" url="http://google.com" />)).to.equal(true);
Such will fail:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A wrong title" url="http://youtube.com" />)).to.equal(true);
This is an older question, but my approach is a little different than the existing answers:
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
ie. You're wanting to check that the component being tested renders another component, and passes the correct prop(s) to it.
So if the component being tested looks something like:
const MyComp = ({ title, url }) => (
<div>
<Title title={title} url={url} />
</div>
)
Then the test could look something like:
import Title from 'path/to/Title';, u
it('renders Title correctly', () => {
const testTitle = 'Test title';
const testUrl = 'http://example.com';
const sut = enzyme.shallow(<MyComp title={testTitle} url={testUrl} />);
// Check tested component rendered
expect(sut.exists).toBeTruthy();
// Find the Title component in the subtree
const titleComp = sut.find(Title); // or use a css-style selector string instead of the Title import
// Check that we found exactly one Title component
expect(titleComp).toHaveLength(1);
// Check that the props that were passed were our test values
expect(titleComp.prop('title')).toBe(testTitle);
expect(titleComp.prop('url')).toBe(testUrl);
});
I generally find Enzyme's functions to be very useful for all kinds of checks about components, without needing other libraries. Creating Sinon mocks can be useful to pass as props to components, to (for example) test that a callback prop is called when a button is clicked.