Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: creating the initial state #26277

Closed
Dave-Patsy opened this issue Mar 1, 2023 · 4 comments
Closed

Bug: creating the initial state #26277

Dave-Patsy opened this issue Mar 1, 2023 · 4 comments
Labels
Resolution: Stale Automatically closed due to inactivity Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@Dave-Patsy
Copy link

React version: "18.2.0"
The bug is failure of state initialization with a function, and not being consistent with the client and server. the fix is to use useEffect with empty dependency to initialize the state when the component is mounted on client.
I undertstand that useEffect is for synchronization, so it makes sense to initialize all states when component mounts.
However, I don't understand why it's not initializing the state with the function and then sending it to the client.

memory.tsx

'use client'

import Link from 'next/link'
import React, {useState,useEffect} from 'react'
import MemoryCard from './memoryCard'
import { cardData } from './memoryData'

type cardType = {
    id: number;
    name: string;
    img: string;
    matched: boolean;
}

function createCards() {
    return(
        [...cardData, ...cardData]
        .sort(()=>0.5-Math.random())
        .map((card) => ({...card, id: Math.random()}))

    )
}

export default function Memory() {
    const [cards, setCards] = useState<cardType[]>(createCards)
    const [score, setScore] = useState(0) 
    const [lives, setLives] = useState(5)
    
    // useEffect(()=>{
    //     setCards(()=>createCards())   
    // },[])


    const shuffleCards = () => {
        setCards(()=>createCards())    
    }

    return (
    <div>
           
            <div className='w-f h-f'>

                <Link href={'/'} >
                    <div>Home</div>
                </Link>
                <h1>Magic Match</h1>
                <h3>score: <span id='result'>{score}</span></h3>
                <h3>lives: <span id="lives">{lives}</span></h3>
                <div className='grid grid-cols-4 w-full h-full justify-center items-center mx-auto'>
                    {cards.map((card)=> (
                        <MemoryCard 
                            
                            key={card.id }
                            card={card}
                        />
                    ))}
                </div>
                <button className='' onClick={() =>shuffleCards()}>New Game</button>
            </div>        
    </div>
  )
}

memoryCard.tsx

'use client'

import Image from 'next/image';
import React from 'react'


type props ={
    card: {
        id: number;
        name: string;
        img: string;
        matched: boolean;
    }
}

export default function MemoryCard({card}:props) {
  return (
    <div className="flex w-full h-64 justify-center items-center ">
        <div>
            <img  src={card.img} alt={'card.name'} title={card.name} />
        </div>

    </div>
  )
}

memoryData.ts

export const cardData= [
    {
        name: 'fries',
        img: '/images/games/memory/french-fries.png',
        matched: false,
    },
    {
        name: 'hamburger',
        img: '/images/games/memory/hamburger.png',
        matched: false,
    },
    {
        name: 'hotdog',
        img: '/images/games/memory/hotdog.png',
        matched: false,
    },
    {
        name: 'milkshake',
        img: '/images/games/memory/milkshake.png',
        matched: false,
    },
    {
        name: 'ice-cream',
        img: '/images/games/memory/ice-cream-cone.png',
        matched: false,
    },
    {
        name: 'pizza',
        img: '/images/games/memory/pizza.png',
        matched: false,
    },
]

React version: "18.2.0"

Steps To Reproduce

  1. Initialize state with function
  2. Pass state as prop to child

Link to code example: https://github.com/Dave-Patsy/stateInitializeError

The current behavior

The server and client props are not concurrent
stateInitializeError

The expected behavior

When I initialize a useState with a function on a client component I expect the server to use the function to initialize the state once, and then then the component to the client, so they are to be concurrent.
The fix is to use useEffect to initialize the state after component is mounted

@Dave-Patsy Dave-Patsy added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Mar 1, 2023
@Dave-Patsy
Copy link
Author

this is an example of passing the initializer function to set state from the react docts beta

import { useState } from 'react';

function createInitialTodos() {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: 'Item ' + (i + 1)
    });
  }
  return initialTodos;
}

export default function TodoList() {
  const [todos, setTodos] = useState(createInitialTodos);
  const [text, setText] = useState('');

  return (
    <>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={() => {
        setText('');
        setTodos([{
          id: todos.length,
          text: text
        }, ...todos]);
      }}>Add</button>
      <ul>
        {todos.map(item => (
          <li key={item.id}>
            {item.text}
          </li>
        ))}
      </ul>
    </>
  );
}

I am not sure why createCards() function is not initializing properly

@Dave-Patsy Dave-Patsy reopened this Mar 3, 2023
@Werter12
Copy link

Werter12 commented Mar 5, 2023

Hi @Dave-Patsy
Looks like your question more Next.js related rather than react. When the client props don't match server props after hydration - it's means that server pre-rendering took place. This is a part of optimisation that Next.js doing even for "client" components. So, basically "client" components is pre-rendered due to ssr. If you want completely "client" component. You should lazily import component with option ssr: false.

import dynamic from 'next/dynamic'

const Memory = dynamic(() => import('./components/memory'), { ssr: false });

I've did it an the problem is gone

Copy link

github-actions bot commented Apr 9, 2024

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

@github-actions github-actions bot added the Resolution: Stale Automatically closed due to inactivity label Apr 9, 2024
Copy link

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Stale Automatically closed due to inactivity Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

2 participants