首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使一个下拉菜单依赖另一个下拉菜单

如何使一个下拉菜单依赖另一个下拉菜单
EN

Stack Overflow用户
提问于 2022-04-29 17:49:55
回答 3查看 210关注 0票数 0

我有一个下拉菜单显示州和县。我要县一级依靠州一级。我正在使用react,javascript,prisma来访问数据库。我把它分开了,这样我就能让各州和各县显示出来,但我不知道如何使它们独立。我认为我需要的是一种改变我的功能的方法,它能带来县的数据。我可以根据选择的状态进行分组。所以,我需要的是在获得被选中的状态以将其发送到我的"byCounty“函数之后。这有可能吗?

menu.js

代码语言:javascript
复制
export default function DropDownMenu(props){
    if(!props.states) return
    return(
        <table>
            <body>
            <select onChange={(e) => { console.log(e.target.value) }}>
                {props.states.map(states=>
                    <option>{states.state}</option>
                )}
            </select>
            <select >
                {props.byCounty.map(byCounty=>
                    <option>{byCounty.county}</option>
                )}
            </select>
            </body>
        </table>
    )
}

functions.js

代码语言:javascript
复制
const states = await prisma.county.groupBy({
        by:["state"],
        where: {
            date: dateTime,
        },
        _sum:{
            cases:true,
        },
    });

 const byCounty = await prisma.county.groupBy({
        by:["county"],
        where: {
            date: dateTime,
            state: 'THIS SHOULD BE THE STATE NAME SELECTED BY USER'
        },
        _sum:{
            cases:true,
        },
    });

const result =JSON.stringify(
        {states:states, byCounty:byCounty},
        (key, value) => (typeof value === 'bigint' ? parseInt(value) : value) // return everything else unchanged
      )
    res.json(result);

index.js

代码语言:javascript
复制
<div className={styles.table_container}>
                    <h2>Teste</h2>
                    <DropDownMenu states={myData?myData.states:[]} byCounty={myData?myData.byCounty:[]}></DropDownMenu>
              </div>

我所拥有的:

EN

回答 3

Stack Overflow用户

发布于 2022-04-29 20:34:25

下面是一个自包含的示例,演示如何从模拟API (异步函数)中“获取”选项,并使用结果呈现一个顶级选项列表,使用所选选项对依赖的选项列表执行相同的操作。代码是注释的,如果有什么不清楚的地方,我可以进一步解释。

为了简单起见,示例不使用州和县,但依赖关系是相同的。

TS游乐场

代码语言:javascript
复制
body { font-family: sans-serif; }
.select-container { display: flex; gap: 1rem; }
select { font-size: 1rem; padding: 0.25rem; }
代码语言:javascript
复制
<div id="root"></div><script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.17.10/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">

// import * as ReactDOM from 'react-dom/client';
// import {
//   type Dispatch,
//   type ReactElement,
//   type SetStateAction,
//   useEffect,
//   useRef,
//   useState,
// } from 'react';

// This Stack Overflow snippet demo uses UMD modules instead of the above import statments
const {
  useEffect,
  useRef,
  useState,
} = React;

// The next section is just a mock API for getting dependent options (like your States/Counties example):

async function getOptionsApi (level: 1): Promise<string[]>;
async function getOptionsApi (
  level: 2,
  level1Option: string,
): Promise<string[]>;
async function getOptionsApi (
  level: 1 | 2,
  level1Option?: string,
) {
  const OPTIONS: Record<string, string[]> = {
    colors: ['red', 'green', 'blue'],
    numbers: ['one', 'two', 'three'],
    sizes: ['small', 'medium', 'large'],
  };

  if (level === 1) return Object.keys(OPTIONS);
  else if (level1Option) {
    const values = OPTIONS[level1Option];
    if (!values) throw new Error('Invalid level 1 option');
    return values;
  }

  throw new Error('Invalid level 1 option');
}

// This section includes the React components:

type SelectInputProps = {
  options: string[];
  selectedOption: string;
  setSelectedOption: Dispatch<SetStateAction<string>>;
};

function SelectInput (props: SelectInputProps): ReactElement {
  return (
    <select
      onChange={(ev) => props.setSelectedOption(ev.target.value)}
      value={props.selectedOption}
    >
      {props.options.map((value, index) => (
        <option key={`${index}.${value}`} {...{value}}>{value}</option>
      ))}
    </select>
  );
}

function App (): ReactElement {
  // Use a ref to track whether or not it's the initial render
  const isFirstRenderRef = useRef(true);

  // State for storing the top level array of options
  const [optionsLvl1, setOptionsLvl1] = useState<string[]>([]);
  const [selectedLvl1, setSelectedLvl1] = useState('');

  // State for storing the options that depend on the selected value from the level 1 options
  const [optionsLvl2, setOptionsLvl2] = useState<string[]>([]);
  const [selectedLvl2, setSelectedLvl2] = useState('');

  // On the first render only, get the top level options from the "API"
  // and set the selected value to the first one in the list
  useEffect(() => {
    const setOptions = async () => {
      const opts = await getOptionsApi(1);
      setOptionsLvl1(opts);
      setSelectedLvl1(opts[0]!);
    };

    if (isFirstRenderRef.current) {
      isFirstRenderRef.current = false;
      setOptions();
    }
  }, []);

  // (Except for the initial render) every time the top level option changes,
  // get the dependent options from the "API" and set
  // the selected dependent value to the first one in the list
  useEffect(() => {
    const setOptions = async () => {
      const opts = await getOptionsApi(2, selectedLvl1);
      setOptionsLvl2(opts);
      setSelectedLvl2(opts[0]!);
    };

    if (isFirstRenderRef.current) return;
    setOptions();
  }, [selectedLvl1]);

  return (
    <div>
      <h1>Dependent select options</h1>
      <div className="select-container">
        <SelectInput
          options={optionsLvl1}
          selectedOption={selectedLvl1}
          setSelectedOption={setSelectedLvl1}
        />
        <SelectInput
          options={optionsLvl2}
          selectedOption={selectedLvl2}
          setSelectedOption={setSelectedLvl2}
        />
      </div>
    </div>
  );
}

const reactRoot = ReactDOM.createRoot(document.getElementById('root')!)
reactRoot.render(<App />);

</script>

票数 0
EN

Stack Overflow用户

发布于 2022-04-29 21:15:34

您可以使用自定义钩子来完成此操作。

关键是,在您的代码中,第二个下拉列表应该在第一个下拉列表的日期观察更改&对这些更改做出反应。在React中,您可以使用useEffect() (大多数情况下)来完成此操作:

代码语言:javascript
复制
useEffect(() => {
  reactingToChanges()
}, [watchedVariable])

在片段里,

  • “状态”API正在查询一个真实的数据源。
  • 我嘲笑了县API (我找不到免费的资源解决方案)
  • 我为县添加了一个简单的缓存机制,这样如果数据已经下载,API就不会被查询

代码语言:javascript
复制
// THE IMPORTANT PART IS IN A COMMENT TOWARDS THE BOTTOM

const { useEffect, useState } = React;

const useFetchStates = () => {
  const [states, setStates] = useState([]);

  const fetchStates = () => {
    const myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

    const urlencoded = new URLSearchParams();
    urlencoded.append("iso2", "US");

    const requestOptions = {
      method: "POST",
      headers: myHeaders,
      body: urlencoded,
      redirect: "follow"
    };

    fetch(
      "https://countriesnow.space/api/v0.1/countries/states",
      requestOptions
    )
      .then((response) => response.json())
      .then(({ data: { states } }) => setStates(states))
      .catch((error) => console.log("error", error));
  };

  if (!states.length) {
    fetchStates();
  }

  return {
    states
  };
};

const useFetchCounties = () => {
  const [countiesByState, setCountiesByState] = useState({});
  const [counties, setCounties] = useState([]);

  const fetchCounties = (state) => {
    if (state in countiesByState) {
      setCounties(countiesByState[state]);
    } else if (state) {
      fetch("https://jsonplaceholder.typicode.com/todos")
        .then((response) => response.json())
        .then((json) => {
          const mappedCounties = json.map(({ id, title }) => ({
            id: `${state}-${id}`,
            title: `${state} - ${title}`
          }));
          setCounties(mappedCounties);
          setCountiesByState((prevState) => ({
            ...prevState,
            [state]: mappedCounties
          }));
        });
    } else {
      setCounties([]);
    }
  };

  return {
    counties,
    fetchCounties
  };
};

const Selector = ({ options = [], onChange, dataType }) => {
  return (
    <select onChange={(e) => onChange(e.target.value)} defaultValue={"DEFAULT"}>
      <option disabled value="DEFAULT">
        SELECT {dataType}
      </option>
      {options.map(({ name, val }) => (
        <option key={val} value={val}>
          {name}
        </option>
      ))}
    </select>
  );
};

const App = () => {
  const { states = [] } = useFetchStates();
  const [selectedState, setSelectedState] = useState("");
  const { counties, fetchCounties } = useFetchCounties();
  const [selectedCounty, setSelectedCounty] = useState("");

  // here's the heart of this process, the useEffect():
  // when the selectedState variable changes, the
  // component fetches the counties (based on currently
  // selected state) and resets the currently selected
  // county (as we do not know that at this time)
  useEffect(() => {
    fetchCounties(selectedState);
    setSelectedCounty("");
  }, [selectedState]);

  const handleSelectState = (val) => setSelectedState(val);
  const handleSelectCounty = (val) => setSelectedCounty(val);
  
  return (
    <div>
      <Selector
        options={states.map(({ name, state_code }) => ({
          name,
          val: state_code
        }))}
        onChange={handleSelectState}
        dataType={"STATE"}
      />
      <br />
      <Selector
        options={counties.map(({ id, title }) => ({
          name: title,
          val: id
        }))}
        onChange={handleSelectCounty}
        dataType={"COUNTY"}
      />
      <br />
      Selected state: {selectedState}
      <br />
      Selected county: {selectedCounty}
    </div>
  );
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
代码语言:javascript
复制
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id="root"></div>

票数 0
EN

Stack Overflow用户

发布于 2022-05-01 16:23:23

你问这个问题的方式导致了对你的问题的不同解释,@muka.gergely@jsejcksn的答案都是非常好的解决方案,但这更符合你真正想要的。因为你只想从选定的州得到值并从后端获取县,你可以这样做:

functions.js

代码语言:javascript
复制
 // change to a function that gets a state as parameter
 const byCounty = async (selectedState) => { 
   return await prisma.county.groupBy({
     by:["county"],
     where: {
       date: dateTime,
       // use the received parameter here to fetch the counties
       state: selectedState
     },
     _sum:{
       cases:true,
     },
   })
 };

menu.js

代码语言:javascript
复制
export default function DropDownMenu(props){
    if(!props.states) return
    return(
        <table>
            <body>
            <select 
              // use the byCounty function with the selected value to fetch the counties
              onChange={ async (e) => { 
                await byCounty(e.target.value) 
              }}
            >
                {props.states.map(states=>
                    <option>{states.state}</option>
                )}
            </select>
            <select >
                {props.byCounty.map(byCounty=>
                    <option>{byCounty.county}</option>
                )}
            </select>
            </body>
        </table>
    )
}

仅此而已,如果你想让这个选项--县和州--一起工作,你也可以在其他答案的背后使用这个想法。希望我帮了你!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72061931

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档