next.js React props未将下拉列表值传递到父组件

iaqfqrcu  于 2023-02-04  发布在  React
关注(0)|答案(1)|浏览(138)

我有一个NextJS13应用程序,它使用Tailwind CSS框架来设计UI,使用Boardgame Atlas API根据几个下拉列表项(类别、玩家数量和花费)为用户获取棋盘游戏。
Home组件是应用的主要组件,负责从API获取棋盘游戏并呈现UI,它使用另外三个组件CategoriesSelect、PlayerCount和SpendSelect来呈现下拉列表。
CategoriesSelect组件从API获取可用的类别,并呈现一个下拉列表以允许用户选择类别。PlayerCount组件呈现一个下拉列表以允许用户选择游戏所需的最小玩家数量。SpendSelect组件呈现一个下拉列表以允许用户选择他们的预算。
用户从下拉列表中选择值后,单击“Find my game”按钮将调用fetchOutput函数。此函数使用所选值向API发出GET请求,API将返回匹配的游戏。如果找到任何游戏,则数组中的第一个游戏将记录到控制台,否则将记录错误消息。
代码可以在GitHub here上找到,Vercel here上有一个实时版本。为了方便起见,我还粘贴了下面的代码:

第.js页(主页)

"use client"
import { useState, useEffect } from 'react';
import CategoriesSelect from "@/components/CategoriesSelect";
import PlayerCount from "@/components/PlayerCount";
import SpendSelect from "@/components/SpendSelect";
import axios from 'axios';

export default function Home() {

  const[formData, setFormData] = useState({
    category: "",
    playerCount: 1,
    spend: 25,
  })

  const fetchOutput = async () => {
    console.log(formData)
    try {
      const { data } = await axios.get(
        `https://api.boardgameatlas.com/api/search?categories=${formData.category}&min_players=${formData.playerCount}&lt_price=${formData.spend}&client_id=EsdgqvppMg`
      );
      if (data.games && data.games.length > 0) {
        console.log(data.games[0]);
      } else {
        console.error('No games found');
      }
    } catch (error) {
      console.error(error);
    }
  };
  
  // useEffect(() => {
  //   console.log(formData)
  //   }, [formData])

  return (
    <main >
      <div className="text-center mt-24 mb-16 text-4xl font-mono">looking for your next boardgame?</div>
      <div className="text-center font-mono px-10 mb-4">
        <span>I want to play a(n) </span>
        <CategoriesSelect 
  formData={formData.category}
  setFormData={setFormData}        />
        <span> game with at least </span>
        <PlayerCount
          formData={formData.playerCount}
          setFormData={setFormData}  
          />
        <span> other player(s). My budget is up to: </span>
        <SpendSelect
        formData={formData.spend}
        setFormData={setFormData}  
        />
        <span> USD</span>
      
      </div>
      <div className="flex">
      <button type="button"
      onClick={fetchOutput}
      className="mx-auto px-6 py-2.5 bg-blue-600 text-white font-medium leading-tight rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out text-xl">🎲 Find my game 🎰</button>
      </div>
    </main>
  )
}

类别选择.jsx

import { useState, useEffect } from 'react';
import axios from 'axios';

const CategoriesSelect = ({formData, setFormData}) => {
  const [categories, setCategories] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        'https://api.boardgameatlas.com/api/game/categories?client_id=EsdgqvppMg'
      );
      setCategories(result.data.categories);
    };
    fetchData();
  }, []);

  return (

    <div className='inline mb-3 xl:w-96'>
    <select className='form-select appearance-none
      px-3
      py-1.5
      text-base
      font-normal
      text-gray-700
      bg-white bg-clip-padding bg-no-repeat
      border border-solid border-gray-300
      rounded
      transition
      text-center
      ease-in-out
      focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none" aria-label="Default select example'
      value={formData}
      onChange={e => setFormData({...formData, category: e.target.value})}
      >
      {categories.map(category => (
        <option key={category.id} value={category.id}>
          {category.name}
        </option>
      ))}
    </select>
    </div>
  );
};

export default CategoriesSelect;

播放机计数.jsx

"use client"

export default function PlayerCount({formData, setFormData}) {

  const options = [1, 2, 3, 4, 5, 6, 7].map(number => (
    <option key={number} value={number}>
      {number}
    </option>
  ));

  return (
    <div className='inline mb-3 xl:w-96'>
    <select
    value={formData}
    onChange={e => setFormData({...formData, playerCount: e.target.value})}

      className='form-select appearance-none
      px-7
      py-1.5
      text-base
      font-normal
      text-gray-700
      bg-white bg-clip-padding bg-no-repeat
      border border-solid border-gray-300
      rounded
      transition
      ease-in-out
      m-0
      focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none'
      aria-label='Default select example'
    >
        {options}
    </select>
  </div>  )
}

支出选择.jsx

"use client"

export default function SpendSelect({formData, setFormData}) {

  const options = [10, 25, 50, 75, 100, 200, 300 ].map(number => (
    <option key={number} value={number}>
      {number}
    </option>
  ));

  return (
    <div className='inline mb-3 xl:w-96'>
    <select
    value={formData}
    onChange={e => setFormData({...formData, spend: e.target.value})}
      className='form-select appearance-none
      px-7
      py-1.5
      text-base
      font-normal
      text-gray-700
      bg-white bg-clip-padding bg-no-repeat
      border border-solid border-gray-300
      rounded
      transition
      ease-in-out
      m-0
      focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none'
      aria-label='Default select example'
    >
        {options}
    </select>
  </div>  )
}

我的挑战:
从子组件更新主页组件中formData的状态时遇到问题。即使更新了从子组件传递的属性,游戏.length仍返回0。有人能帮助我从子组件更新formData状态的正确方法吗?

sqyvllje

sqyvllje1#

已修复!答案:
之前:
onChange={e => setFormData({...formData, playerCount: e.target.value})}
之后:
onChange={e => setFormData(prevFormData => ({...prevFormData, playerCount: e.target.value}))}
这应该应用于所有使用onChange的组件。看起来我是在传播一个prop而不是一个object数组。

相关问题