reactjsremixremix.run

Remix: How to use a controlled input in form?


I‘m trying to get my feet wet with Remix. Building forms with simple HTML inputs works great. But how can I use more complex inputs with custom UI, for example a tag list input or a date range picker? Usually these are controlled inputs - how can I get their values in a Remix action? I haven‘t found anything about this in their docs.


Solution

  • This isn't really a remix, or even react specific problem as it applies anytime you want to use a custom control with a native form, but what I would do here is create a hidden input inside of your form control component

    const MyCustomControl = (props) => (
        <div>
            <input type="hidden" name={props.name} value={props.value} />
            {/* the rest of your component here */}
        </div>
    )
    

    note that when passing value in the DOM you need a string so if you are storing the state of your control some other way then it needs to be serialized as a string however you want to handle that on the server side. Also, this example is written to be a stateless component but you could of course do something similar with a stateful input component.

    Also I would recommend making the input not hidden until javascript loads client side so that people without JavaScript can use a normal input as a fallback. You would choose the best input type that you can of course. Note that this also means that the serialization format has to be human readable as they a human could be the one editing it if they don't have JS. Something like:

    const MyCustomControl = (props) => {
        const [loaded, setLoaded] = useState(false);
        useEffect(() => setLoaded(true), []);
    
        return (
            <div>
                <input type={loaded ? "hidden" : "text"} name={props.name} value={props.value} />
                {loaded && (
                    <div>{/* the rest of your component here */}</div>
                )}
            </div>
        )
    }
    

    Edit: It just occurred to me that Remix (or even React itself, it's always changing lol), may have some better way of knowing if the client side JS has loaded. If so, please use that instead of what I did here, but the basic idea is the same