如何在 React 中映射 2 个数组并渲染到 Table

How to map over 2 Array in React and render to Table

我正在创建一个股票应用程序,允许用户通过调用股票来搜索股票价格 API 并搜索任何股票并取回其价格。

我将价格存储在 table 上,但是我希望也能够将用户输入存储到 table 中。

我的策略是将用户的搜索输入存储到数组“symbolArray”中,并将 api 信息存储到“dataArray”中。

这是我的问题。我能够映射“dataArray”并将其渲染到 table。但是我需要映射“symbolArray”并将其渲染到 table 那已经渲染了“数据数组”中的项目。

这是我目前所拥有的

const Quotes = ()=> {

//I call their states
const [symbolArray, setSymbolArray] = useState([])
const [dataArray, setDataArray] = useState([])


//this function stores, and calls the api data that the user searches for


function getData() {

  fetch(url)
        .then(res => res.json())
        .then(data =>{
          
            dataArray.push(data)
            
        })
        
}

// this is also activated when the user searchs. their input is pushed to the array of stock ticker symbols

const addingStuff = ()=> {
  symbolArray.push(input)

}


return (

<>

{/* here a user searches for stock */}

<div class="input-group">
                <input id="searchBar" type="search" class="form-control rounded search-bar" placeholder="Enter Ticker Symbol" aria-label="Search"
                   aria-describedby="search-addon" value={input} onInput={e => setInput(e.target.value)} />
                <button type="button" class="searchButton btn p-2  bg-succ" id="searchButton" onClick={()=> {getData(); addingStuff(); }}>Quote This Stock</button>
              </div>

{/* here is my table */}
<table class='table'>
                  <thead>
                    <tr>
{/*  table  headers*/}
                      <th scope='col'>Symbol</th>
                      <th scope='col'>Current Price</th>
                      <th scope='col'>Day High</th>
                      <th scope='col'>Day Low</th>
                      <th scope='col'>Price Change</th>
                      <th scope='col'>Open Price</th>
                      <th scope='col'>Percentage Change</th>
                      <th scope='col'>Previous Close</th>
                    </tr>
                  </thead>
                  <tbody>

{/* i call the function that gets and stores the data */}

                  {getData ? 
                    dataArray.map((stock, index) => {
                      const {c, d, dp, h, l, o, pc} = stock;
                      return (
                        <tr key={index}>

{/* here is the issue where the 2 arrays clash  */}
                         <th scope='row'>{symbolArray.map((symbol, i) => { return i})}</th>
                          <td>{c}</td>
                          <td>{d}</td>
                          <td>{dp}</td>
                          <td>{h}</td>
                          <td>{l}</td>
                          <td>{o}</td>
                          <td>{pc}</td>
                        </tr>
                      )
                    })
  
                     : null }


</>

}



当你使用像 React 这样的 library/framework 时,最好将显示与控件分离。在您的情况下,这意味着:

  • table 是演示文稿。它应该不知道数据来自哪里(来自 symbolArraydataArray - 或两者)。
  • symbolArraydataArray和他们的联合就是对照。这不需要“知道”数据的呈现方式(它可以是 ordered/unordered 列表、table 或显示为卡片元素的简单数据迭代器)。

考虑到这一点,我认为您应该将解决方案分为两部分:

  • 一部分负责获取和处理数据
  • 另一部分负责在table.
  • 中显示一组数据

这是执行此操作的代码段:

const { useState, useEffect } = React

// custom hook for mocking API data
const useTypicode = () => {
  const [response, setResponse] = useState(null);

  // only runs once
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(json => {
        setResponse(() => json.map(({id, name, username, email}) => ({id, name, username, email})))
      })
  }, []);

  return response;
};

// AddUser doesn't know what is happening when the
// button is clicked: only sends the current state
// as an argument for the function it received
// in props
const AddUser = (props) => {
  const [name, setName] = useState(null)
  const [userName, setUserName] = useState(null)
  const [email, setEmail] = useState(null)
  
  return (
    <div>
      Name: <input type="text" onInput={(e) => setName(e.target.value)}/><br />
      Username: <input type="text" onInput={(e) => setUserName(e.target.value)}/><br />
      Email: <input type="text" onInput={(e) => setEmail(e.target.value)}/><br />
      <button
        onClick={() => props.addUser({name, userName, email})}
      >
        ADD USER +
      </button>
    </div>
  )
}

// Table doesn't "know" where the data comes from
// API, user created - doesn't matter
const Table = (props) => {
  const headers = Object.keys(props.userList[0])
  return (
    <table>
      <thead>
        <tr>
          {
            headers.map(header => <th key={header}>{header}</th>)
          }
        </tr>
      </thead>
      <tbody>
        {
          props.userList.map(user => <tr key={user.id}>{Object.values(user).map((val, i) => <td key={user.id + i}>{val}</td>)}</tr>)
        }
      </tbody>
    </table>
  )
}

// App doesn't know how the custom data is being
// entered or how it is displayed - only knows
// how to update the two lists it handles and
// where to pass them on
const App = () => {
  const apiUsers = useTypicode()
  const [users, setUsers] = useState([])
  
  const addUser = (userData) => {
    setUsers(prevState => [
      ...prevState,
      {
        id: Date.now(),
        ...userData
      }
    ])
  }
  
  return (
    <div>
      <AddUser
        addUser={(userData) => addUser(userData)}
      /><br />
      {
        apiUsers &&
          <Table
            userList={[...apiUsers, ...users]}
          />
      }
      
    </div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<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>

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

对于代码片段中的一些“错误做法”感到抱歉(例如,在 AddUser 中的 JSX 元素中定义函数),但基本逻辑是我想说明的:不要考虑 HTML table 作为一个存储任何东西的实体。不,HTML table 只显示你喂它的东西。 “播放”JS 中的数据(合并不同的来源、按 key/value 筛选、排序等),并且演示文稿 (table) 应该更新(因为它提供了一组新数据可以显示)。这是反应性。