我试图根据子组件的子组件中给出的输入来更新一些
useState()
变量的值,但在调用它时不断收到错误,指出 setItem()
中的 setValue()
或 useState()
函数声明不是函数。
这是声明和引用它们的父级代码:
import React, {useEffect, useState} from "react"
import "./Creation.css"
import Header from "../components/Header"
import Footer from "../components/Footer"
import CreatorSurface from "../components/Creator/CreatorSurface"
import ProfilePic from "../assets/profile.png"
// import { AttributeContextProvider, useAttributeContext } from "../contexts/AttributeContext"
export default function Creation() {
const [strength, setStrength] = useState("10")
const [dexterity, setDexterity] = useState("10")
const [constitution, setConstitution] = useState("10")
const [intelligence, setIntelligence] = useState("10")
const [wisdom, setWisdom] = useState("10")
const [charisma, setCharisma] = useState("10")
const [item, setItem] = useState()
const [value, setValue] = useState()
useEffect(() => {
switch (item) {
case "Strength":
setStrength(value)
break;
case "Dexterity":
setDexterity(value)
break;
case "Constitution":
setConstitution(value)
break;
case "Intelligence":
setIntelligence(value)
break;
case "Wisdom":
setWisdom(value)
break;
case "Charisma":
setCharisma(value)
break;
default:
console.log("selector not recognized")
}
}, [item, value])
return (
<>
<Header />
<div className="creator">
<h1 className="creator-title">Character Creator</h1>
<div className="creator-banner">
<img className="profile" alt="Profile" src={ProfilePic}/>
<div className="character-details">
<h1 className="character-name">Character Name</h1>
<h2>race class</h2>
</div>
<div className="banner-atr">
<label for="str">Strength: </label>
<p name="str">{strength}</p>
<label for="dex">Dexterity: </label>
<p name="dex">{dexterity}</p>
<label for="con">Constitution: </label>
<p name="con">{constitution}</p>
<label for="int">Intelligence: </label>
<p name="int">{intelligence}</p>
<label for="wis">Wisdom: </label>
<p name="wis">{wisdom}</p>
<label for="cha">Charisma: </label>
<p name="cha">{charisma}</p>
</div>
</div>
<CreatorSurface setItem={setItem} setValue={setValue}/>
</div>
<Footer />
</>
)
}
“中间人”代码:
import React, { useState } from "react"
import CreatorDetails from "./CreatorDetails"
import CreatorClasses from "./CreatorClasses"
import CreatorRaces from "./CreatorRaces"
import CreatorEquipment from "./CreatorEquipment"
import "./CreatorSurface.css"
export default function CreatorSurface({ setItem, setValue }) {
const [isSelected, setIsSelected] = useState()
const handleClick = (e) => {
const id = e.target.id;
setIsSelected(id);
console.log(id);
};
const renderChild = () => {
switch (isSelected) {
case "details":
return <CreatorDetails setItem={setItem} setValue={setValue}/>
case "race":
return <CreatorRaces />
case "class":
return <CreatorClasses />
case "equipment":
return <CreatorEquipment />
default:
return <CreatorDetails />
}
}
return (
<div className="parent">
<div className="selector">
<h1>Create your character using the sections below</h1>
<button onClick={handleClick} id="details" >Details</button>
<button onClick={handleClick} id="race" >Race</button>
<button onClick={handleClick} id="class" >Class</button>
<button onClick={handleClick} id="equipment" >Equipment</button>
<button onClick={handleClick} id="save" >Save Character</button>
<button onClick={handleClick} id="sheet" >Character Sheet</button>
</div>
{renderChild()}
</div>
)
}
以及我想要更新的组件:
import React, {useState} from "react";
import "./CreatorSub.css"
import Details from "./Details/Details";
// import { useAttributeContext } from "../../contexts/AttributeContext"
export default function CreatorDetails({ setItem, setValue }) {
// list of options
const options = [
{
label: '16',
value: '16'
},
{
label: '14',
value: '14'
},
{
label: '14',
value: '14-1'
},
{
label: '12',
value: '12'
},
{
label: '12',
value: '12-1'
},
{
label: '10',
value: '10'
}
]
// list of select names
const selectNames = [
"Strength",
"Dexterity",
"Constitution",
"Intelligence",
"Wisdom",
"Charisma"
]
const [chosenOptions, setChosenOptions] = useState({});
// remove the option when chosen by another selector
const isChosenByOther = (optionValue, selectName) => {
for (let key in chosenOptions) {
if (key !== selectName) {
if (chosenOptions[key] === optionValue) {
return true;
}
}
}
return false;
};
const handleChange = (ev) => {
setChosenOptions({...chosenOptions, [ev.target.name]: ev.target.value});
let value = ""
if (ev.target.value === "14-1") {
value = "14"
} else if (ev.target.value === "12-1") {
value = "12"
} else {value = ev.target.value}
setItem(ev.target.name)
setValue(value)
};
// Runs through the list of selector names provided above and creates the neccessary elements with corresponding labels
const selectRender = selectNames.map((name, index) => {
return (
<>
<label for={name}>{name}:</label>
<select name={name} key={index} onChange={handleChange} value={chosenOptions[name] || ''}
required={index === 0} id={name}>
<option value=''/>
{options.filter(({value}) => !isChosenByOther(value, name))
.map(({label, value}, oIndex) =>
<option value={value} key={oIndex}>{label}</option>)
}
</select>
</>
)
})
return (
<div className="sub-parent">
<div className="sub-selector">
<div id="char-info">
<label for="char-name">Name:</label>
<input type="text" id="char-name" name="char-name" />
<label for="level">Level:</label>
<select name="level" id="level" type="submit">
<option value="1">1</option>
<option value="1">2</option>
<option value="1">3</option>
<option value="1">4</option>
<option value="1">5</option>
<option value="1">6</option>
<option value="1">7</option>
<option value="1">8</option>
<option value="1">9</option>
<option value="1">10</option>
<option value="1">11</option>
<option value="1">12</option>
<option value="1">13</option>
<option value="1">14</option>
<option value="1">15</option>
<option value="1">16</option>
<option value="1">17</option>
<option value="1">18</option>
<option value="1">19</option>
<option value="1">20</option>
</select>
</div>
<div id="attributes" >
<h1>Character Attributes</h1>
<p>Characters have 6 Defining Scores: Strength (Str), Constitution (Con), Dexterity (Dex),
Intelligence (Int), Wisdom (Wis), and Charisma (Cha). As your character will essentially
be a combination of statistics and ability scores, it is important to define which scores
should be higher or lower to match what type of character you want to play.
</p>
<p>Each character will be given the same pool of 6 numbers to choose from to divide between
the Defining Scores (be aware that your choice of race will increase or decrease different scores);
16, 14, 14, 12, 12, and 10.
</p>
<div className="atr-selectors">
{selectRender}
</div>
</div>
</div>
<Details />
</div>
)
}
当我通过
setItem
函数调用 setValue
组件中的 CreatorDetails
和 handleChange
函数时,我收到错误,它不是函数。希望当 <select>
组件中的 CreatorDetails
标签更改值时,它将立即反映在 Creation
组件中,因为它们位于同一页面视图上。不确定是什么阻止了它将其识别为函数。任何帮助将不胜感激。
在您的
renderChild
方法中,默认情况下不会为 setItem
和 setValue
属性传递任何值。由于 isSelected
被初始化为 null
,默认情况就是最初渲染的情况。
建议更改:
const renderChild = () => {
switch (isSelected) {
case "details":
return <CreatorDetails setItem={setItem} setValue={setValue} />
case "race":
return <CreatorRaces />
case "class":
return <CreatorClasses />
case "equipment":
return <CreatorEquipment />
default:
return <CreatorDetails setItem={setItem} setValue={setValue} />
}
}
PS——这是说明 TypeScript 为何有价值的教科书示例。如果您为
CreatorDetails
属性指定了类型,您的 IDE 会在适当的位置显示错误;)