【问题标题】:React input onChange issue using Context API使用 Context API 响应输入 onChange 问题
【发布时间】:2021-09-14 13:19:04
【问题描述】:

我正在制作一个简单的加密货币应用程序,只是为了使用 Coingecko API 检查价格和一些信息 在我的反应应用程序中,我决定使用全局上下文来管理硬币数据,使用 Coingecko API 中的 axios 获取,我还实现了一个搜索输入,用户可以在其中输入名称来过滤表中的数据,这个 SearchBar 组件的问题是,一旦我在浏览器上初始化应用程序,表中就没有显示任何内容,我必须对输入进行更改才能开始显示数据 我的代码如下

从硬币上下文开始:

CoinsState.js

import { React, useReducer } from "react";
import coinsReducer from "./CoinsReducer";
import coinsContext from "./CoinsContext";
import axios from "axios";

export default function CoinsState(props) {
const initialState = {
 coins: [],
};
const [state, dispatch] = useReducer(coinsReducer, initialState);

const getData = async () => {
const res = await axios.get(
  "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false"
);

  dispatch({
    type: "GET_COINS",
    payload: res.data,
  });
};
return (
  <coinsContext.Provider
    value={{
      coins: state.coins,
      getData,
    }}
  >
    {props.children}
   </coinsContext.Provider>
 );

}

CoinsContext.js

import { createContext } from "react";  
const coinsContext = createContext()
export default coinsContext

types.js 用于 CoinsReducer.js

export const GET_COINS = "GET_COINS";

CoinsReducer.js

import { GET_COINS, } from "../types";


 export default (state, action) => {
 const { payload, type } = action;

 switch (type) {
   case GET_COINS:
     return {
       ...state,
       coins: payload,
     };

   default:
     return state;
 }

};

现在我的反应组件

SearchBar.js

import { useState, useContext, useEffect } from "react";
import coinsContext from "./context/Coins/CoinsContext";
export default function SearchBar({ handleFilterCoins }) {
 const { coins } = useContext(coinsContext);
 const [search, setSearch] = useState("");

useEffect(() => {
   handleFilterCoins(coins, search);
 }, [search]);

 const handleOnChange = (e) => {
   setSearch(e.target.value);
 };
 return (
   <input
     type="text"
    placeholder="Start typing to get results"
    className="form-control bg-dark text-light border-0 mt-4 text-center w-100 "
    autoFocus
     value={search}
     onChange={(e) => handleOnChange(e)}
   />
 );

}

TableCoin.js

import React, { useContext, useState, useEffect } from "react";
import coinsContext from "./context/Coins/CoinsContext";
import CoinRow from "./CoinRow";
import SearchBar from "./SearchBar";
const TableCoins = () => {
 const { coins, getData } = useContext(coinsContext);

 useEffect(() => {
   getData();
 }, []);

 const [filteredCoins, setfilteredCoins] = useState(coins);

 const titles = ["#", "Coin", "Price", "Price Change", "24hs Volume"];

 const handleFilterCoins = (coins, filter) => {
   setfilteredCoins(
     coins.filter((coin) =>
       coin.name.toLowerCase().includes(filter.toLowerCase())
     )
   );
 };
 return (
   <div className="container">
    <SearchBar handleFilterCoins={handleFilterCoins}></SearchBar>
     <table className="table table-dark mt-4 table-hover ">
      <thead>
        <tr>
          {titles.map((title) => (
            <td>{title}</td>
           ))}
         </tr>
       </thead>
       <tbody>
        {filteredCoins.map((coin, index) => (
           <CoinRow coin={coin} key={coin.id} index={index + 1}></CoinRow>
         ))}
       </tbody>
     </table>
    </div>
 );
};

export default TableCoins;

CoinRow.js

import React from "react";

const CoinRow = (props) => {
  const { coin, index } = props;
  return (
   <tr>
     <td>{index}</td>

     <td className="fs-3">
       <span>
         <img
           src={coin.image}
           alt={coin.name}
           style={{ width: "15%" }}
           className="me-3 img-fluid"
         />
       </span>
       {coin.name}
       <span className="ms-3 text-muted text-uppercase fs-4">
         {coin.symbol}
       </span>
     </td>

    <td>{coin.current_price}</td>
     <td
       className={coin.price_change_24h > 0 ? "text-success" : "text-danger"}
     >
       {coin.price_change_24h}
     </td>
    <td>{coin.total_volume}</td>
   </tr>
 );

};

export default CoinRow;

终于App.js

import "./App.css";
import CoinsState from "./components/context/Coins/CoinsState";
import TableCoins from "./components/TableCoins";
function App() {

  return (
    <CoinsState>
       <div className=" row ">
         <TableCoins></TableCoins>
       </div>
    </CoinsState>
  );
}

 export default App;

我真的不明白这个问题是怎么回事,我试图将过滤后的硬币作为全局硬币上下文的一部分进行管理,但最终结果相同,硬币数据在应用程序启动后没有立即显示,但在对 SearchBar 执行更新后会这样做

但是,如果我将 SearchBar insdie App.js 视为普通的 html 输入,通过状态管理其值并将搜索输入作为道具传递给 TableCoins,这不是问题

App.js

 import "./App.css";
 import { useState } from "react";
 import CoinsState from "./components/context/Coins/CoinsState";
 import TableCoins from "./components/TableCoins";
 function App() {
 const [search, setSearch] = useState("");

 return (
   <CoinsState>
     <div className="container">
     <input
           type="text"
           placeholder="Search a Coin"
           className="form-control bg-dark text-light border-0 mt-4 text-center w-100 "
           autoFocus
          onChange={(e) => setSearch(e.target.value)}
         />
       <div className=" row ">
         <TableCoins search={search}></TableCoins>
       </div>
     </div>
   </CoinsState>
 );

}

导出默认应用;

TableCoins.js

import React, { useContext, useEffect } from "react";
import coinsContext from "./context/Coins/CoinsContext";
import CoinRow from "./CoinRow";

const TableCoins = ({ search }) => {
 const { getData, coins } = useContext(coinsContext);

 useEffect(() => {
   getData();
   // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

 const titles = ["#", "Coin", "Price", "Price Change", "24hs Volume"];

  const filteredCoins = coins.filter((coin) =>
  coin.name.toLowerCase().includes(search.toLowerCase())
 );
  return (
  <table className="table table-dark mt-4 table-hover ">
  <thead>
    <tr>
      {titles.map((title) => (
        <td>{title}</td>
      ))}
    </tr>
  </thead>
  <tbody>
    {filteredCoins.map((coin, index) => (
      <CoinRow coin={coin} key={coin.id} index={index + 1}></CoinRow>
    ))}
  </tbody>
</table>

); };

 export default TableCoins;

我认为这与 useState 挂钩的异步工作方式有关,但如果您能对此有所了解,我将不胜感激

【问题讨论】:

  • 这是很多代码,你能把它缩小到你认为与问题相关的部分吗?
  • 我认为问题出在 Searchbar.js 特别是在 useEffect 中的回调函数 handleFilterCoins 中,可能它也与 CoinsState.js有关>

标签: javascript reactjs


【解决方案1】:

我终于想出了解决这个问题的办法。 我又给了 Context 方法来解决这个问题的机会

CoinState.js 内部,我在状态上设置了另外 2 个字段

const initialState = {
    coins: [],
    filteredCoins: [],
    filter: '',
};

filter用于跟踪用户的实际输入,filteredCoins保存过滤过程的结果

const filterCoins = (search) => {
    const result = state.coins.filter((coin) =>
        coin.name.toLowerCase().includes(search.toLowerCase())
    );
    dispatch({
        type: "SET_FILTERED_COINS",
        payload: result,
    });
};

const setFilter = (filter) => {
    dispatch({
        type: "SET_FILTER",
        payload: filter,
    });
};

SET_FILTERED_COINSSET_FILTER 是在 types.js 中定义的类型,使用它们自己的 reducer 来操作上下文状态

现在,在SearchBar.js 内部,这个上下文被消费了,使用UseEffect 钩子我们可以跟踪过滤器值并在每次更改时触发过滤过程。

import React, {useEffect, useContext} from 'react';
import coinsContext from './context/Coins/CoinsContext';

const SearchBar = () => {
    const {setFilter, filter, filterCoins} = useContext(coinsContext)
    useEffect(() => {
        filterCoins(filter)
    }, [filter]);
    return (
        <input
            type="text"
            placeholder="Search a Coin"
            className="form-control bg-dark text-light border-0 mt-4 text- 
center w-100 "
            autoFocus
            onChange={(e) => setFilter(e.target.value)}
        />
    );
}

export default SearchBar;

【讨论】:

    猜你喜欢
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2019-01-29
    • 2021-02-11
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多