javascriptreactjssolid-js

Solidjs reactivity on arrays


I can't figure out why the below code reacts this way with solidjs. In the function handleFileChange, if I update the array by modifying its elements field directly, solidjs detects the change. But if I replace the element, nothing happens. This is really strange to me. I need to understand why it is like this

function Test() {
    const [files, setFiles] = createSignal([{ f: null }, { f: null }]);

    const handleFileChange = (index: number, event: any) => {
        const file = event.target.files[0]
        const newFiles = [...files()];
        // newFiles[index].f = file;        // This works as expected.
        newFiles[index] = {f:file}       // Nothing happens here. ????
        setFiles(newFiles);
    };
 

    return (
        <div>
            <For each={files()}>
                {(_, index) => (
                    <div>
                        <input
                            type="file"
                            onChange={(event) => handleFileChange(index(), event)}
                         />
     
                    </div>
                )}
            </For>
        </div>
    );
}

Solution

  • Both ways of doing rerun your dependents because you are creating a new array with [...files()].

    The mystery comes from the fact that assigning, or not, a file to this element should have no incident on the input rendering (I have tested commenting out both update lines).

    Note that the behavior is the same with a text input.

    Using the DevTools, we can notice that the whole Test component gets rerendered on change.

    So my interpretation of the problem is that the equality check ({ equals: (prev, next) => ... }) is bugged by whether the object in the array is the same or not than before. JavaScript can use references for objects, and declaring a whole new object (= {f:file}) changes that reference, whereas changing the property (.f = file) keeps the reference.

    The components rerenders according to this check, that is why you can experience some problems with inputs, where the value gets erased.

    You can raise attention to this issue by filing an issue on GitHub : https://github.com/solidjs/solid/issues/new/choose.


    Please note that you can update your files using the following syntax:

    setFiles((f) => {
        const newFiles = [...f];
        newFiles[index].f = file;
        return newFiles;
    });
    

    This method ensures that your update function is pure, does not rely on a state.