【问题标题】:A default dropdown value only renders after an initial input默认下拉值仅在初始输入后呈现
【发布时间】:2021-02-06 01:19:23
【问题描述】:

我有一个带有下拉菜单的输入表单,我正在尝试设置默认值。该值是从父组件传递下来的,并且是从异步函数(我认为可能有一部分)接收的。 async 函数在 h1 中呈现用户名并正确显示在表单上方。它还作为根据人名确定默认值的条件。我曾尝试在多个组件中使用状态,但总是以“错误:重新渲染过多。 React 限制了渲染的数量以防止无限循环。”请看下面并帮助我弄清楚,如果您能提供一些关于我在这里缺少的概念的主题,我们将不胜感激。

const dashboard = (props)=>{
    const getName = async ()=>{
        try{~GETS DATA~
            setName(parsedData[0].user_name}
        catch{error}
    }

    let defaultSchool
 //switch statement chooses the value for the default in dropdown menu based on the name from GET
    switch(name){
        case "user1":
        defaultSchool= "Cool School"
        case "user2":
        defaultSchool= "Another School
    }
  return(
      <div>
          <InputForm defaultSchool= defaultSchool />
      </div>
  )
}

上面是父组件,它将这些数据向下传递到包含输入表单的下一个组件。

const InputForm = ({defaultSchool})=>{
    const [school, setSchool]= useState(defaultSchool)
    const [some_state, setSome_State] = useState("")
    // numerous other states associated with the form with no significant relevance

        const onSubmitForm = async e => {
            e.preventDefault()
            try{~normally POSTS data~}
            catch{error}
        }
     
      return(
             <h1 className="text-center my-5 input 
                                   title">Input Form</h1>
             <form className="mt-5" onSubmit={onSubmitForm}>
             {/* DATE  */}
                    <input type="date"  name="connection_date" 
                                      placeholder="Date of Contact" 
                                       className="form-control" 
                                       value={connection_date} 
                         onChange={e => setDate(e.target.value)}/>
             {/* SCHOOL  */}
                 <select  type="text" name="school" 
                                       placeholder="School" className="form-control mt-3" 
                                       defaultValue={defaultSchool} value={defaultSchool} 
                                       onChange={e => setSchool(e.target.value)}>
                         // <option value="NADA" disabled>Choose your school</option>
                         // this is a default option i tried with defaultValue to stop the first 
                            value in the dropdown from showing which was unsuccessful also
                             to show but didn't work at all either
                         <option value="Cool School"> Cool School </option>
                         <option value="Another School">Another School </option>
                 <button className="btn btn-success btn-block mt-3 mb-5">Add</button>
              </form>
      )
}

最初,默认学校显示为下拉列表中的选定项目;我点击添加按钮,学校没有提交。但是,在第一次单击添加按钮后,会发生正确的行为,并且每次单击都会提交学校。关于这个问题有很多 SO 帖子,但没有一个包含 React 和钩子,也没有提供任何关于有条件地呈现下拉列表默认选项的见解。

完整的代码示例:

 import React, { Fragment, useState, useEffect } from 'react'
 //components
 import InputConnection from './connectionlist/InputConnection'
 import ListConnections from './connectionlist/ListConnections'
 import LogoutBtn from './LogoutBtn'
 import ReportingLayout from './reporting/Layout/ReportingLayout'

const Dashboard = ({ setAuth }) => {
const [name, setName] = useState("")
const [allConnections, setAllConnections] = useState([])
const [connectionsChange, setConnectionsChange] = useState(false)
const [timeDay, setTimeDay] = useState("Good Morning,")
const auth = setAuth

const getName = async () => {
    try {
        const response = await fetch("/dashboard/", {
            method:"GET", 
            headers:{ token: localStorage.token }
        })

        const parseData = await response.json()
        // console.log(parseData)
    if (parseData.admin === 'lead') {
        setName("Lead School Counselor")
        setAllConnections(parseData.results)
    }else{
        setName(parseData[0].user_name)
        setAllConnections(parseData)
    }
    } catch (error) {
    
    }
}

const greeting = () => {
    let date = new Date()
    const hours = date.getHours()
    if(hours < 12){
        setTimeDay("Good Morning, ")
    }else if (hours >= 12 && hours < 17){
        setTimeDay("Good Afternoon, ")
    }else{
        setTimeDay("Good Evening, ")
    }
}

useEffect(() => {
    getName()
    greeting()
    setConnectionsChange(false)
}, [connectionsChange, timeDay])

if(name === "Lead School Counselor" ){
    return(
        <div>
            <ReportingLayout auth={auth} allConnections={ allConnections } />    
        </div>
       )
}else{
    let defaultSchool
    switch(name) {
      case 'Jim Smith':
        defaultSchool= "Cool School"
        break;
      case "Bob Williams":
        defaultSchool= "Another School"
        break;
  }

return(
    <Fragment>
        <div className="container">
            <div className='btn-group '>
                <LogoutBtn setAuth = {setAuth}/>
            </div>
                <h1 className="d-flex mt-3 pl-3" > {timeDay} 
    {name}&nbsp;&nbsp;</h1>
                <InputConnection defaultSchool={defaultSchool} 
setConnectionsChange={setConnectionsChange}/>
                <ListConnections allConnections={ allConnections } 
setConnectionsChange={setConnectionsChange}/> 
            </div>     
        </Fragment>
    )
}
}

导出默认仪表板;

下一个组件:

import React, { Fragment, useState } from "react";
import { toast } from 'react-toastify'

const InputTodo = ({ defaultSchool, setConnectionsChange }) => {

  const today = new Date()
  var formattedDate = today.toISOString().substr(0,10);
  const [contact_type, setContactType] = useState("");
  const [contact_method, setContactMethod] = useState("");
  const [provision, setProvision] = useState("");
  const [connection_date, setDate] = useState(formattedDate);
  const [student_id, setStudentID] = useState("");
  const [purpose, setPurpose] = useState("");
  const [gender, setGender] = useState("");
  const [yearGroup, setYearGroup] = useState("");
  const [referral_discharge, setReferralDischarge] = useState("");
  const [cp_referral, setCPReferral] = useState("");
  const [school, setSchool] = useState(defaultSchool);

  const onSubmitForm = async e => {
    e.preventDefault();
    try {
      const myHeaders = new Headers();
      myHeaders.append("Content-Type", "application/json");
      myHeaders.append("token", localStorage.token);

     const body = { 
                  contact_type, 
                  contact_method, 
                  provision,
                  connection_date,
                  student_id,
                  purpose,
                  gender,
                  yearGroup,
                  school, 
                  referral_discharge, 
                  cp_referral
                };
  const response = await fetch("/dashboard/connections", {
    method: "POST",
    headers: myHeaders,
    body: JSON.stringify(body)
  });

  const parseResponse = await response.json();

  console.count(parseResponse);

  setConnectionsChange(true);
  setContactType("")
  setContactMethod("")
  setProvision("")
  setDate(formattedDate)
  setStudentID("")
  setPurpose("")
  setGender("")
  setYearGroup("")
  setSchool(defaultSchool)
  setReferralDischarge("")
  setCPReferral("")

  toast.success('Contact has been added', {
    position: "top-center",
    autoClose: 3000,
    hideProgressBar: false,
    closeOnClick: true,
    pauseOnHover: true, 
    draggable: true,
    });

    } catch (error) {
      console.error(error.message);
    }
 };
return (
    <Fragment>
      <h1 className="text-center my-5 input-title">Communication Log</h1>
      <form className="mt-5" onSubmit={onSubmitForm}>
    {/* DATE  */}
    <input type="date"  name="connection_date" placeholder="Date of Contact" 
    className="form-control" value={connection_date} onChange={e => 
    setDate(e.target.value)}/>
    {/* SCHOOL  */}
    <select  type="text" name="school" placeholder="School" className="form- 
    control mt-3" defaultValue={defaultSchool} value={defaultSchool} 
    onChange={e => setSchool(e.target.value)}>
        <option value="NADA" disabled>Choose your school</option>
      <optgroup label= "SEN">
          <option value="Cool School">Cool SChool</option>
          <option value= "Another School">Another School</option>
      
      </optgroup>
    </select>
    {/* SCHOOL ID */}
    <input type="text" name="student_id" placeholder="Student ID" 
    className="form-control mt-3" value={student_id} onChange={e => 
    setStudentID(e.target.value)}/>
    {/* YEAR / GROUP  */}
    <select type="text" name="yeargroup" placeholder="year/group" 
className="form-control mt-3" value={yearGroup} onChange={e => 
setYearGroup(e.target.value)}>
      <option value="DEFAULT">Choose the Year/ Group</option>
      <option value="yr01">yr01</option>
      <option value="yr02">yr02</option>
      <option value="yr03">yr03</option>
      <option value="yr04">yr04</option>
      <option value="yr05">yr05</option>
      <option value="yr06">yr06</option>
      <option value="yr07">yr07</option>
      <option value="yr08">yr08</option>
      <option value="yr09">yr09</option>
      <option value="yr10">yr10</option>
      <option value="yr11">yr11</option>
      <option value="yr12">yr12</option>
    </select>
    {/* GENDER  */}
    <select type="text" name="gender" placeholder="gender" className="form-control mt-3" value={gender} onChange={e => setGender(e.target.value)}>
      <option value="DEFAULT">Gender</option>
      <option value="M">M</option>
      <option value="F">F</option>
    </select>
    {/* REFERRAL OR DISCHARGE */}
    <select type="text" name="referral_discharge" placeholder="referral_discharge" className="form-control mt-3" value={referral_discharge} onChange={e => setReferralDischarge(e.target.value)}>
      <option value="DEFAULT">Was this a referral, continuation or discharge?</option>
      <option value="referral">Referral</option>
      <option value="discharge">Discharge</option>
      <option value="continuation">Continuation</option>
    </select>
  
    {/* CONTACT TYPE  */}
    <select type="text" name="contact_type" placeholder="contact_type" className="form-control mt-3" value={contact_type} onChange={e => setContactType(e.target.value)}>
      <option value="DEFAULT">Type of Contact</option>
      <option value="student">student</option>
      <option value="parent">parent</option>
      <option value="emergeny contact">emergeny contact</option>
      <option value="staff">staff</option>
      <option value="social worker">social worker</option>
      <option value="support staff">support staff</option>
      <option value="SENCO">SENCO</option>
      <option value="other">other</option>
    </select>

    {/* CONTACT METHOD  */}
    <select type="text" name="contact_method" placeholder="contact_method" className="form-control mt-3" value={contact_method} onChange={e => setContactMethod(e.target.value)}>
    <option value ="DEFAULT">Contact Mode/ Method</option>
      <option value="in-person">in-person</option>
      <option value="text">text</option>
      <option value="whatsapp">whatsapp</option>
      <option value="phone call">phone call</option>
      <option value="email">email</option>
      <option value="check-in">check-in</option>
      <option value="classroom presentation">classroom presentation</option>
      <option value="session">session</option>
      <option value="video chat">video chat</option>
      <option value="group">group session</option>
      <option value="crisis intervention">crisis intervention</option>
      <option value="home visit">home visit</option>
      <option value="sbst, mdt, case conference">sbst, mdt, case conference</option> 
      <option value="outside agency meeting">outside agency meeting</option>
      <option value="other meeting">other meeting</option>
      
    </select>
    {/* CP_REFERRAL  */}
    <select type="text" name="cp_referral" placeholder="cp_referral" className="form-control mt-3" value={cp_referral} onChange={e => setCPReferral(e.target.value)}>
      <option value="DEFAULT">Was this a CP Referral?</option>
      <option value="Yes">Yes</option>
      <option value="No">No</option>
    </select>
    {/* <div className='mt-3'>
      <p>Is this a CP REFERRAL?</p>
      <div className="form-check-inline">
          <input className="form-check-input" type="radio" name="cp_referral" id="y" value={cp_referral} onClick={(e)=> setCp_referral(e.target.value)}/>
          <label className="form-check-label">Yes</label>
      </div>

      <div className="form-check-inline">
        <input className="form-check-input" type="radio" name="cp_referral" id="n" value={cp_referral} onClick={(e)=> setCp_referral(e.target.value)}/>
        <label className="form-check-label">No</label>
      </div>
    </div> */}
    {/* PURPOSE  */}
    <textarea className="form-control mt-3" name="purpose" placeholder="What was the purpose of the connection?" value={purpose} onChange={e => setPurpose(e.target.value)}></textarea>
    {/* PROVISION  */}
    <textarea className="form-control mt-3" name="provision" placeholder="Provision/Support Agreed Upon/ Plan Forward (When Necessary) / Any Additional Notes" value={provision} onChange={e => setProvision(e.target.value)}></textarea>
    {/* BUTTON  */}
    <button className="btn btn-success btn-block mt-3 mb-5">Add</button>
  </form>
</Fragment>

); };

导出默认 InputTodo;

【问题讨论】:

  • 请分享一个完整的代码示例。
  • 您似乎在询问至少几个问题,请将 SO 问题限制为单个特定问题,并提供足够的代码供其他人理解并查看问题所在。更多的代码总比不够好,但太多的代码和不够一样糟糕。我怀疑你的 useEffect 正在调用 greeting() 导致渲染循环。 greeting 更新了效果的依赖数组中列出的timeDay,但不清楚原因,因为它没有在效果中使用。
  • 没有主要问题是为什么它需要两次点击才能正常运行;就是这样。
  • name 在初始渲染之后 是否总是“Jim Smith”或“Bob Williams”之一? defaultSchool 在初始渲染中未定义。 InputConnectionInputTodo 之间还有什么,或者它们实际上是同一个组件?你能澄清一下你所说的学校被提交与否是什么意思吗?您是否只是再次单击“添加”,没有与表单字段交互,并且表单按预期工作?您能否创建一个 running 代码框来重现此问题?

标签: reactjs forms react-hooks async.js


【解决方案1】:

我之前在手动设置value 属性的下拉菜单中遇到过这个问题。发生的情况是,无论如何,下拉菜单中总会有一个可见的选项。如果传入value,则应显示该值。但是如果你传入undefined,那么列表中的第一个选项就会变成可见选项。 看起来该选项已被选中,但实际上并未被选中。这就是为什么如果您选择另一个选项然后返回到第一个选项,它将起作用,因为您现在实际上已经选择了它。

const [school, setSchool] = useState(defaultSchool)

useState 的初始状态只设置了一次。它不会响应您的 defaultSchool 属性中的更改。因此,如果它首先收到undefined 的值,那么school 将保持undefined,直到您调用setSchool,即使您稍后获得defaultSchool 的有效值。

解决此问题的一种方法是使用 useEffect 挂钩来监听 defaultSchool 的更改。

useEffect( () => {
    if ( defaultSchool & ! school ) {
        setSchool( defaultSchool );
    }
}, [defaultSchool, school]);

【讨论】:

  • 这也是我怀疑正在发生的事情,尽管使用defaultValuevalue 道具使问题更加复杂。对于onChange 处理程序,我假设OP 希望这是一个受控输入,因此该值应该反映状态而不是传递的道具,即它应该是value={school}。通过使用value={defaultSchool},虽然输入看起来像改变了(它会),但不是状态,正如您正确指出的那样。
猜你喜欢
  • 1970-01-01
  • 2020-06-07
  • 2011-10-02
  • 1970-01-01
  • 2020-05-01
  • 2021-11-22
  • 1970-01-01
  • 2020-01-24
  • 1970-01-01
相关资源
最近更新 更多