passing mobx-react-form object as a prop - reactjs

I'm trying to pass mobx-react-form object as a prop to a function component I created. the problem is when I call my component like that
<NameEditor form={ newFolderForm }/>
I do get the form argument inside NameEditor component but it doesn't let me 'edit' the field .. its like the field isn't editable,
but when I call the component like that
{ NameEditor({ form: newFolderForm }) }
it works perfectly fine, what am I missing ? shouldn't the both ways be the same thing in function components ?
edit: here is how I fetch the form
const NameEditor = ({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
)
thanks

make sure you are using observer() on you're function component, from the code you showed there I think you missed this part.
const NameEditor = observer(({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
))
https://mobx.js.org/refguide/observer-component.html
read how it works with Stateless function components

Related

How to link a text input with associated error message in a reusable React component

I have a React component called TextInput which looks like this:
const TextInput = React.forwardRef<HTMLInputElement, ITextInputProps>(
({ error, ...props }, ref) => {
return (
<>
<input
type="text"
aria-describedby="" // šŸ‘ˆ see here
ref={ref}
{...props}
/>
{error && (
<p> // šŸ‘ˆ how can I reference this?
{error}
</p>
)}
</>
)}
)
For accessibility, I am trying to link the error message to the field using aria-describedby.
In a normal HTML document I would just use the id of the paragraph tag, but with reusability in mind, I would like to learn how to do this in a React-like way.
The use of refs comes to mind, but I'm not sure how to apply it in this scenario.
I would say that the best way to do this is to create a required prop that handles the ID and you apply that ID on the aria fields.
With that in mind, if what you wanted is to generate the data dinamically you could use a useEffect running it only once and setting a state inside it.
function Component() {
const [describedBy, setDescribedBy] = useState()
useEffect(() => {
setDescribedBy(generateRandomString());
}, [])
return (
<div aria-describedby={describedBy}></div>
)
}

how do I change a input children value component?

Iā€™m wondering how can I change a children input value from main?
Basically I have a component that Iā€™m sending a children, and in this component I want to be able to change the input value.
Here is my main:
<FooterOptions>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
And here is my component:
export function FooterOptions(props:{children: ReactNode}){
return(
<div>
{props.children}
</div>
)
}
The children prop is something that you can only render onto the page, so there's nothing you can do with it to change the value of the input.
Instead how I would think about this is that you want to provide a mechanism for FooterOptions to change the value of another component. Here it happens to also be rendered as its children, but it would work the same even if it was rendered someplace else.
const Parent = () => {
const updateInput = (val) => setState(val)
return (
<FooterOptions handleUpdate={updateInput}>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
)
}
export function FooterOptions(props:{children: ReactNode, handleUpdate}){
// Example handler
const handleClick = () => handleUpdate('updated inside FooterOptions')
return(
<div onClick={handleClick}>
{props.children}
</div>
)
}
If you'd like to add more details of how you are hoping to update, then I can add a better example šŸ˜€

Multiple value / onChange values in a React form

I must be missing something very simple, but I can't figure out how to deal with having 2 value / onChange to pass to my component.
I tried changing the names, and that gets rid of errors and renders the app, but of course event.target.value does not work as if I change the second value to, for example to numval or something. event.target.numval doesn't recognize anything is happening.
Thank you so much in advance! And if this has been asked before I apologize, but I couldn't find it... which makes me think I'm overlooking a very simple solution.
return (
...
<PersonForm
onSubmit={addName}
value={newName}
onChange={handleName}
value={newNumber}
onChange={handleNumber}
/>
)
Here is the original code that worked fine before I tried to put the component into its own file:
return (
...
<form onSubmit={addName}>
<div>
name: <input value={newName} onChange={handleName} />
</div>
<div>
number: <input value={newNumber} onChange={handleNumber} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
your code sample is not clear but it seems you want to retrieve value from event.target object through event handler function triggered by onChange event on
an element. And you want to call event handler function available in a parent component from a child component.
You can try the following:
const App () => {
const [newNameValue, setName] = useState('');
const [newNumberValue, setNumber] = useState('');
const nameChangeHandler = (event) => {
setName(event.target.value);
};
const numberChangeHandler = (event) => {
setNumber(event.target.value);
};
const submitFormHandler = () => {
your code on form submit
};
return (
<PersonForm
submitForm={submitFormHandler}
newName={newNameValue}
handleName={nameChangeHandler}
newNumber={newNumberValue}
handleNumber={numberChangeHandler}
/>
);
}
const PersonForm (props) => {
return (
<form onSubmit={props.submitForm}>
<div>
name: <input value={props.newName} onChange={props.handleName} />
</div>
<div>
number: <input value={props.newNumber} onChange={props.handleNumber} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
);
}
onChange is a reserved attribute name which is available on few html elements
such as <input> and <select>. You should ideally not use it on other elements, like in your case custom html component <PersonForm>.
Since you're keeping form in separate component then you can trigger a function call received through attributes on props object.
You may call two different onChange event handler functions on two input elements.
I hope you got your answer, feel free to ask in case of any more doubts.
You may refer:
https://reactjs.org/docs/forms.html

Component instance as default prop

I want to allow a component to pass in a button component or default to an instance created in the component.
Say I have a component like this:
const Form = ({ submitButton, children }) =>
<form>
{childrem}
{submitButton || <OtherComponent/>}
</form>
How can I achieve this?
You can pass button component to the props and simply use it in jsx:
const Form = ({ SubmitButtonComponent, children }) =>
<form>
{childrem}
{SubmitButtonComponent ? <SubmitButtonComponent /> : <OtherComponent />}
</form>
I think this would be a good use case for ES2015 default function parameters:
const Form = ({ SubmitButtonComponent = OtherComponent, children }) =>
<form>
{children}
<SubmitButtonComponent/>
</form>
This should be semantically equivalent to the answer given above.

React : Is this possible to replace a "placeholder template tag" send as children?

I have for example this code below :
<AjaxForm>
<input type="hidden" name="xxx" value="xxx" />
<div className="grid">
<div className="gdcol-xs-11">
[[SUBMIT_BUTTON]]
</div>
<div className="gdcol-xs-11">
[[CANCEL_BUTTON]]
</div>
</div>
</AjaxForm>
And I would like, for example, be able in the AjaxForm component to replace the tag placeholder 'SUBMIT_BUTTON' by this :
<a href="javascript:void(0);" onClick={this.handleSubmit}>VALIDATE</a>
Is there a way to do this by iterating on this.props.children in the AjaxForm component ?
Is this possible to find some text pattern by crawling all the children ?
Should I have to use refs or a key ?
Thank you in advance !
---- EDIT
To add some informations, this is the render of the AjaxForm Component
return (
<form action="" method="post" ref={this.ajaxRef} id={this.props.id} onSubmit={this.onSubmit}>
<input type="hidden" name="form_id" value={this.props.id} />
{this.props.children}
<input type="submit" value="" className="fake-submit" />
<div id={("ajax-") + this.props.id + ("-messages-container")} className="ajax-form-messages"></div>
</form>
)
I think I understand your issue now. You're generating a component inside AjaxForm and you want to be able to place that component dynamically. In that case you should create another component called AjaxFormContents (or whatever your specific form should be called) which receives your generated component via props and places it wherever you want.
// AjaxForm.js
...
render() {
return React.cloneElement(this.props.children, {
submitButton: this.generateSubmitButton() // or however you're doing it
})
}
...
Now whatever component you put as a child will have access to this component.
// AjaxFormContents.js
...
render() {
return ( // arrange your form contents however you like!
<div>
<input />
{ this.props.submitButton }
</div>
)
}
Then in your parent component:
// Parent.js
...
render() {
return (
<AjaxForm>
<AjaxFormContents />
</AjaxForm>
)
}
This should work, however another approach -- using a higher order component (HOC) would be a nice solution here as well, because your AjaxForm doesn't display anything, it just wraps your form contents.. instead AjaxForm can be an HOC which passes a generated submit button component to the wrapped component.
var ajaxForm = function (WrappedComponent) {
return React.createClass({
generateSubmitButton() {
return <a>your special submit button </a>
},
render() {
<WrappedComponent submitButton={ this.generateSubmitButton() } />
}
})
}
Then you can have the exact same AjaxFormContents component as above and when you export it:
// or module.exports, whichever one you're using.
export default ajaxForm(AjaxFormContents)

Resources