2

I recently picked up Next.js, and before going into any backend stuff, I took a look over React and many things needed of it.

In short, I have built a form, which will take the input of the user, use it in the API and return me a JSON Object, that will edit some of my Components that I will later show. So I used state. I use an auxiliary variable to set up a boolean state, that will be set to true once my Components will be edited. Now, my Components are stored in an array, that I will show after it got edited later. Here's the code

to avoid confusion, showResults is another state that works as intended. my issues are with showMatch and setShowMatch

function Form() {

    // adding state to the HeadingText Component to hide it once the user presses the Search button
    const [showResults, setShowResults] = React.useState(true)
    
    // adding state to the auxiliary variable to show my array later after edit
    const [showMatch, setShowMatch] = React.useState(false)

    let matches = []

    // adding the form handling section, with async event to query the API
    const registerUser = async event => {
        event.preventDefault() // don't redirect the page

        // hiding the Component HeadingText, so we show only the data of the games
        setShowResults(false)

        // calling the API in api/register, passing the link of the game or anything we would like to pass
        const res = await fetch('/api/register', {
            body: JSON.stringify({ matchLink: event.target.matchLink.value }),
            headers: { 'Content-Type': 'application/json' },
            method: 'POST'
        })

        //querying the real API, printing out the result
        const result = await res.json()
        console.log(result)

        //preparing my array of components
        for (let i = 0; i < 3; i++) {
            matches.push(<Match
                title = { `game   ${i} `}
                nOfGame = {i}
                game = {result.rounds[i]}
            />);
        }

        setShowMatch(true);
        console.log('I have changed the state, I check if it is true or false, if true: print my array, else: print null')
        console.log(showMatch ? matches : null)
        console.log('I will print my array to double check it\'s value')
        console.log(matches)
    }

    return (
        <>
            { /* Showing or not the component Heading Text */ }
            { showResults ? <HeadingText /> : null }
            
            <div className="form-container">
                <form onSubmit={registerUser}>
                    <input type="text" name="matchLink" id="textFiledId" placeholder="insert a demo" />
                    <input type="submit" value="Search" name="subDemo" />
                </form>
            </div>

            { /* Showing or not the component of the match */ }
            <div className="match-holder">
                { showMatch ? matches : null } 
            </div>

            <p>{matches.length}</p> {/* This should be 0 */ }
        </>
    )
}

export default Form

I am not understanding how I may be able to show my array of Components, as this message in the console https://i.stack.imgur.com/B4pap.jpg is telling me my array is full of components. I am 100% sure I am missing something but after some time running over the state doc of React, I couldn't find an example that helped

(A question I have looked at Correct modification of state arrays in React.js)

noxter
  • 186
  • 1
  • 11

1 Answers1

2

You are using a local non-state variable matches in your component which means no matter how you update it in registerUser function, everytime there is a state variable update, it will trigger a re-render and matches will again be initialized to []

What you need is a new state variable initialized as [], and you can set it in your registerUser function. It will retain its value upon re-renders unless explicitly changed.

Storing components in state has performance implications, rather store the required props for mentioned array of components as array of objects in state and render your component using it.

Here is a sample implementation

function Form() {

    // adding state to the HeadingText Component to hide it once the user presses the Search button
    const [showResults, setShowResults] = React.useState(true)

    const [matches, setMatches] = React.useState([])

    // adding the form handling section, with async event to query the API
    const registerUser = async event => {
        event.preventDefault() // don't redirect the page

        // hiding the Component HeadingText, so we show only the data of the games
        setShowResults(false)

        // calling the API in api/register, passing the link of the game or anything we would like to pass
        const res = await fetch('/api/register', {
            body: JSON.stringify({ matchLink: event.target.matchLink.value }),
            headers: { 'Content-Type': 'application/json' },
            method: 'POST'
        })

        //querying the real API, printing out the result
        const result = await res.json()
        console.log(result)

        let nmatch = []

        //preparing my array of components
        for (let i = 0; i < 3; i++) {
            nmatch.push({
                title: `game   ${i} `
                nOfGame: i
                game: result.rounds[i]
            });
        }
        setMatches(nmatch)
    }

    return (
        <>
            { /* Showing or not the component Heading Text */ }
            { showResults ? <HeadingText /> : null }

            <div className="form-container">
                <form onSubmit={registerUser}>
                    <input type="text" name="matchLink" id="textFiledId" placeholder="insert a demo" />
                    <input type="submit" value="Search" name="subDemo" />
                </form>
            </div>

            { /* Showing or not the component of the match */ }
            <div className="match-holder">
                { matches.length > 0 && (
                  matches.map(m => (
                    <Match
                      key={m.title}
                      {...m}
                    />
                  ))
                )}
            </div>
            <p>{matches.length}</p> {/* This should be 0 */ }
        </>
    )
}

export default Form
Hamza Asif
  • 347
  • 1
  • 9
  • So here the code sets up an empty array as a state, that I will change after my API call in the registerUser function. Once it's done and I have my data, it will change the state of the array and re-render it? Is this correct? – noxter Sep 20 '21 at 07:00
  • 1
    Yes. This is correct. – Hamza Asif Sep 20 '21 at 10:12
  • 1
    Thanks for your example and explanation, this helped me with the issue and corrected my code, glad you helped me – noxter Sep 20 '21 at 10:33