Search code examples
reactjsnext.jsbuildtypeerror

Error at building in next 13 with params destructuration


I'm getting this error when I run npm run build with next 13.1.2 in my Words component when I try to asign to a const the words from an array of a local json.

TypeError: Cannot read properties of undefined (reading '%5Bdifficulty%5D') 

I'm using for the first time the next framework, and this error only shows when I run the build command of next, but in development it works without throwing any error.I know that is because when it destructures the level and the difficulty from the params instead of recieving for example just 'easy' or the level string like '1' I'm getting this strange string like %5Bdifficulty%5D or %Blevel%5D . I don't know why this could be happening.

This is where I receive the params of the url that is /levels/[level]/[difficulty]

\levels\[level]\[difficulty]\page.jsx
export default function page({ params }) {
  const { level, difficulty } = params;

  return (
    <div className='level-container'>
      <BackButton path={`/levels/${level}`} />
      <Words level={parseInt(level)} difficulty={difficulty} />
    </div>
  );
}

And this is the Words component where I get the error.

//@components/GameComponents/Words.jsx
import SingleWord from './SingleWord';
import levelsData from '@/data/levels.json';
import WordsProvider from '@/contexts/WordsContext';
import { UseLevels } from '@/contexts/LevelsContext';
import LevelUnlocked from '../LevelUnlocked';

export default function Words({ level, difficulty }) {
  const words = levelsData[level - 1][difficulty];
  const { newLevelUnlocked } = UseLevels;

  return (
    <WordsProvider>
      <div className="words-container">
        {words.map((word) => (
          <SingleWord
            word={word}
            key={word}
            level={level}
            difficulty={
              difficulty === 'easy' ? 1 : difficulty === 'medium' ? 2 : 3
            }
          />
        ))}
        {newLevelUnlocked && <LevelUnlocked />}{' '}
        {/*Alert the user that a new level has been unlocked*/}
      </div>
    </WordsProvider>
  );
}

And finally this is my json file.

//data/levels.json
[
  {
    "index": 0,
    "level" : 1,
    "easy" : ["Work", "Play", "Milk", "Game", "Year"],
    "medium" : ["Space", "Apple", "Water", "Beach", "Arrow"],
    "hard": ["Finger", "Little", "Combat", "Cloudy", "Health"]
  },
  {
    "index": 1,
    "level" : 2,
    "easy" : ["Fist", "Stop", "Tree", "Ride", "Iron"],
    "medium" : ["Ocean", "Train", "Write", "Close", "Civil"],
    "hard": ["Dolphin", "Emotion", "Phrases", "Strings", "Accords"]
  },
  {
    "index": 2,
    "level" : 3,
    "easy" : ["Jump", "Lost", "True", "Mind", "Rude"],
    "medium" : ["Grass", "Bunny", "Party", "Rugby", "Storm"],
    "hard": ["Doctor", "Little", "Accord", "Sponge", "Suffix"]
  },
  {
    "index": 3,
    "level" : 4,
    "easy" : ["Brown", "Civil", "Gests", "Sleep", "Clock"],
    "medium" : ["Hunger", "Pencil", "Shield", "Ginger", "Travel"],
    "hard": ["Academy", "Elegant", "Scholar", "Miracle", "Victory"]
  }
]

At the first I thought that could have been because I was passing as level prop a string instead of number, then I converted it into an integer but it doesn't work. I also tried to console log in the build a difficulty like this: levelsData[0][difficulty]

And it seems to console log correctly, so the problem is with the level and difficulty params that I get in the page.jsx component I suppose.


Solution

  • //levels/level/[level]/[difficulty]/page.jsx

    Since I'm new in the Next framework world I didn't know about that you need to generate your static parameters when you want to have static site generation. So I need to create at build time that parameters with generateStaticParams. So I added this reserved function of next js 13 in this component.

    export async function generateStaticParams () {
      const difficulties = ['easy', 'medium', 'hard']
      return difficulties.map(diff => levelsData.map(lvl => ({
        level: lvl.level.toString(),
        difficulty: diff
      }))).flat()
    }
    

    So in this way I create a site for each level, and each level with all the difficulties. I decided to go for the bottom up way.