如何在 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 是演示文稿。它应该不知道数据来自哪里(来自
symbolArray
或 dataArray
- 或两者)。
symbolArray
、dataArray
和他们的联合就是对照。这不需要“知道”数据的呈现方式(它可以是 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) 应该更新(因为它提供了一组新数据可以显示)。这是反应性。
我正在创建一个股票应用程序,允许用户通过调用股票来搜索股票价格 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 是演示文稿。它应该不知道数据来自哪里(来自
symbolArray
或dataArray
- 或两者)。 symbolArray
、dataArray
和他们的联合就是对照。这不需要“知道”数据的呈现方式(它可以是 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) 应该更新(因为它提供了一组新数据可以显示)。这是反应性。