Home > OS >  Prevent change of dropdown options when underlying data changes
Prevent change of dropdown options when underlying data changes

Time:05-27

With the code below:

<!DOCTYPE html>
    <html>
        <body>
            <head>
                <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
                <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
                <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
                <style>
                </style>
            </head>
            <body>
                <div id="root">
            </body>
            <script type="text/babel">

                const api = {
                    data: [
                        {
                            id: 1, party: 'Zuckerberg', news: [
                                {id: 1, headline: 'Zuckerberg news1'},
                                {id: 2, headline: 'Zuckerberg news2'},
                            ]
                        },
                        {
                            id: 2, party: 'Musk', news: [
                                {id: 1, headline: 'Musk news1'},
                                {id: 2, headline: 'Musk news2'},
                            ]
                        },
                        ]
                }

                const App = () => {
                    const [data, setData] = React.useState([])

                    React.useEffect(() => setData(api.data), [])

                    const handleSelectChange = (e) => {
                        const name = e.target.value
                        setData(prev => prev.filter(datum => datum.party === name))
                    }

                    return (
                        <div>
                            <select
                                onChange={handleSelectChange}>
                                {data.map(datum => <option key={datum.id}>{datum.party}</option>)}
                            </select>
                            {data.map(
                                datum => 
                                    datum.news.map(newsItem =>
                                        <div key={newsItem.id}>{newsItem.headline}</div>
                                    )
                            )}
                        </div>
                    )
                }
                ReactDOM.render(<App />, document.getElementById('root'))
            </script>
    </html>

how can I prevent the reduction of dropdown options upon dropdown change, preferably without changing the api.data structure? I basically need this dropdown to be refreshed only when api.data returns new data.

Is it possibly while keeping only single state (data)?

CodePudding user response:

I've modified your code below.

Main points:

  • removed useEffect - the API is static so doesn't need to be re-allocated
  • useState only monitors the filter (filtering of the API data only happens when the state changes so there isn't a need to store this - but you could useMemo if you wanted and make it dependent on the data)
  • The options are derived from api.data

<!DOCTYPE html>
    <html>
        <body>
            <head>
                <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
                <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
                <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
                <style>
                </style>
            </head>
            <body>
                <div id="root">
            </body>
            <script type="text/babel">

                const api = {
                    data: [
                        {
                            id: 1, party: 'Zuckerberg', news: [
                                {id: 1, headline: 'Zuckerberg news1'},
                                {id: 2, headline: 'Zuckerberg news2'},
                            ]
                        },
                        {
                            id: 2, party: 'Musk', news: [
                                {id: 1, headline: 'Musk news1'},
                                {id: 2, headline: 'Musk news2'},
                            ]
                        },
                        ]
                }

                const App = () => {
                    const [data, setData] = React.useState(api.data.length?api.data[0].party:undefined)
                    
                    const handleSelectChange = (e) => {
                        setData(e.target.value)
                    }

                    return (
                        <div>
                            <select
                                onChange={handleSelectChange}>
                                {api.data.map(datum => <option key={datum.id}>{datum.party}</option>)}
                            </select>
                            {api.data.filter(datum=>datum.party===data).map(
                                datum => 
                                    datum.news.map(newsItem =>
                                        <div key={newsItem.id}>{newsItem.headline}</div>
                                    )
                            )}
                        </div>
                    )
                }
                ReactDOM.render(<App />, document.getElementById('root'))
            </script>
    </html>

CodePudding user response:

I can't think of a way to do it without using another state variable. What's the reason for only wanting one variable?

You could use an object state variable as Qubaish said but that can be much more difficult to maintain than just using a second state variable.

If you can get away with using two state variables use the following:


const App = () => {
    const [data, setData] = React.useState([]);
    const [options, setOptions] = useState([]);

    React.useEffect(() => {
        setData(api.data);
        setOptions(api.data);
    }, []);

    const handleSelectChange = (e) => {
        let name = e.target.value;
        setData(options.filter((datum) => datum.party === name));
    };

    return (
        <div>
            <select onChange={handleSelectChange}>
                {options.map((datum) => (
                    <option key={datum.id}>{datum.party}</option>
                ))}
            </select>
            {data.map((datum) =>
                datum.news.map((newsItem) => (
                    <div key={newsItem.id}>{newsItem.headline}</div>
                ))
            )}
        </div>
    );
};

It just stores a copy of the potential options in the options state and then stores the data to be displayed in "data".

CodePudding user response:

One way to handle this scenario by define state as an object like I mentioned in my comment. This might help you. It's working for me.

const MyComponent = () => {
  const [data, setData] = React.useState({})

  React.useEffect(() => setData({dropdownValues: api.data, filteredValues: api.data}), [])

  const handleSelectChange = (e) => {
      const name = e.target.value
      const filteredValues = data.dropdownValues.filter(datum => datum.party === name);
      setData({...data, filteredValues})
  }

  return (
      <div>
          <select
              onChange={handleSelectChange}>
              {data.dropdownValues && data.dropdownValues.map(datum => <option key={datum.id}>{datum.party}</option>)}
          </select>
          {data.filteredValues && data.filteredValues.map(
              datum => 
                  datum.news.map(newsItem =>
                      <div key={newsItem.id}>{newsItem.headline}</div>
                  )
          )}
      </div>
  )
}
  • Related