0
votes

I'm new in ReactJS and I'm still trying to understand the lifecycle of the components. I've been trying to set the default value of a multi react-select for sometime, but somehow it simply doesn't work. Since I'm following the 'react-select' documentation exactly the way it says, I think that my problem is relationed to React's components lifecycle.

What I'm trying to do: The objective is to make a 'edit user' screen. The multi select is where will be setted the technologies that the User works with. One User can have lots of techs.

(I'm posting here only the code that is related to the problem, making my problem easier to understand)

Getting all 'Techs' from the database:

const [allTechs, setAllTechs] = useState([]);

useEffect(() => {
    async function getAllTechs(){
        const response = await api.get('/api/techs');
        
        return response.data.docs;
    }

    getAllTechs().then(result => {
        const list = [];

        result.forEach(r => {
            let tech = {value: r._id, label: r.name};

            list.push(tech);
        })

        setAllTechs(list);
    });
}, []);

Getting User's techs from the database:

const [techs, setTechs] = useState([]);

let { id } = useParams();

useEffect(() => {
    async function getData() {
        const response = await api.get(`api/users/${id}`);
        
        return response.data;
    }

    getData().then(result => {
        setTechs(result.techs);
    });
}, []);

After that, I'm creating a State called 'userTechsForSelect', that should be an array of objects. Those objects must have only 2 atributes: 'value' and 'label', just like the State called 'allTechs', 'cause those are the atributes that 'react-select' is asking on it's documentation. (https://react-select.com/home)

Populating 'userTechsForSelect':

const [userTechsForSelect, setUserTechsForSelect] = useState([]);

useEffect(() => {
    async function getInitialTechValues(){
        const list = [];

        techs.map(t => {
            let tech = {value: t._id, label: t.name};
            list.push(tech);
        })

        setUserTechsForSelect(list);
    }

    getInitialTechValues();

}, [techs]);

Now, when I intantiate the 'Select' on my component, I use the States mentioned previously ('allTechs' as 'options' and 'userTechsForSelect' as 'defaultValue') to set its atributes:

return(
    <Select
        className="editUserReactSelect"
        options={allTechs}
        defaultValue={userTechsForSelect[0]}
        closeMenuOnSelect={false}
        placeholder="Select"
        isMulti
        styles={colourStyles}
    />
)

The options are being setted, but the default value is not. My guess is: the 'Select' component is being rendered before 'userTechsForSelect' receives its values. But if a put a 'console.log(userTechsForSelect)' before the 'Select' tag, I can see that the component is being rendered many times, and while the state is empty on first renders, it is not on the last ones.

The component exactly the way it's being rendered: [Img]Select

Console.log result: [Img]Console.log

Can anyone help me and tell why are the default values not being setted? If you want me to post any other code, just ask! Thank you!

Edit 1:

Even when I put the defaultValueof the Select as an array, it keeps not displaying the techs that the User has.

<label className="editUserLabel">Tecnologias
    {console.log(userTechsForSelect[0])}
    <Select
          className="editUserReactSelect"
          options={allTechs}
          defaultValue={[userTechsForSelect[0]]}
          closeMenuOnSelect={false}
          placeholder="Selecione"
          isMulti
          styles={colourStyles}
     />
</label>

But, when I change the defaultValue to a fixed object (like I'm showing bellow), it works (even if I don't put it as an array, like defaultValue={exampleValue[0]})

const exampleValue = [{value: '5e6067ddef8a973de092403d', label: 'NodeJS'}];

return(
    <label className="editUserLabel">Tecnologias
        {console.log(userTechsForSelect[0])}
         <Select
             className="editUserReactSelect"
             options={allTechs}
             defaultValue={exampleValue[0]}
             closeMenuOnSelect={false}
             placeholder="Selecione"
             isMulti
             styles={colourStyles}
         />
     </label>
)

The result is: here

Edit 2:

As asked, I'm going to console.log the options State and the defaultValue State before the Select, so you can see that the defaultValue should be matching with one of the options.

The code:

<label className="editUserLabel">Tecnologias
    {console.log("allTechs: ")}
    {console.log(allTechs)}
    
    {console.log("userTechsForSelect: ")}
    {console.log(userTechsForSelect)}
    <Select
        className="editUserReactSelect"
        options={allTechs}
        defaultValue={[userTechsForSelect[0]]}
        closeMenuOnSelect={false}
        placeholder="Selecione"
        isMulti
        styles={colourStyles}
    />
</label>

(The console.log's are going to appear many times, 'cause the component renders many times as well, so I put another console.log before each one to make it easier to see)

The result: [Img]console.log results

As you can see, the userTechsForSelect array is empty in the beginning, but then it is not. I think that the Select component is being rendered before userTechsForSelect gets its values

2
Can you add what userTechsForSelect and allTechs look like please?larz
Instead of posting an answer, you should edit this post to add context. I was asking because I was curious if the value of the objects in userTechsForSelect[0] matches any value in allTechs.larz
Sorry, I did't see the "edit" button, I'm gonna edit from now on.João Bonsegno

2 Answers

0
votes

Since you've passed isMulti as true, your defaultValue should be an array

defaultValue={[userTechsForSelect[0]]}
0
votes

I just solved it!

As I was suspecting, the component was being rendered before userTechsForSelect had any value. So I created a function that verifies if userTechsForSelect.length > 0 and returns the component only if this condition is satisfied

function EditUser() {
    // All useStates and useEffects that I posted before

    // What I modified:
    function createReactSelect(){
        if (userTechsForSelect.length > 0){
            return (
                <label className="editUserLabel">Tecnologias         
                    <Select
                        className="editUserReactSelect"
                        options={allTechs}
                        defaultValue={[userTechsForSelect[0]]}
                        closeMenuOnSelect={false}
                        placeholder="Selecione"
                        isMulti
                        styles={colourStyles}
                    />
                </label>
            );
        }
    }

    return {createReactSelect()};
}

export default EditUser;