无法使用文本输入值更新React状态

时间:2020-11-12 09:46:29

标签: javascript reactjs react-hooks state

我想做的是我想从API获取员工列表,将其保存在状态中,然后按员工姓名进行“实时”搜索。

我苦恼的是我无法使用过滤后的数组更新状态。当我开始在搜索字段中输入内容时,员工会进行过滤,但是一旦删除了一些字母,就什么都不会改变。

如果我的.map()不是状态,而是包含过滤数组的变量,则一切正常。这在某种程度上与状态和状态更新有关。

这是我的代码:

import "./App.css";
import React, { useState, useEffect } from "react";
import styled from "styled-components";

const Container = styled.div`
  width: 1280px;
  max-width: 100%;
  margin: 0 auto;
  th {
    text-align: left;
    padding: 10px;
    background: #f5f5f5;
    cursor: pointer;
    :hover {
      background: #ddd;
    }
  }
  td {
    border-bottom: 1px solid #f5f5f5;
    padding: 5px;
  }
`;
const TopHeader = styled.div`
  display: flex;
  justify-content: space-between;
  padding: 20px;
  input {
    width: 400px;
    padding: 10px;
  }
`;

function App() {
  const [employees, updateEmployees] = useState([]);

  if (employees == 0) {
    document.title = "Loading...";
  }

  useEffect(() => {
    fetch("http://dummy.restapiexample.com/api/v1/employees")
      .then(res => res.json())
      .then(result => {
        updateEmployees(result.data);
        document.title = `Total: ${result.data.length} `;
      });
  }, []);

  const [searchValue, updateSearch] = useState("");

  const filteredEmpl = employees.filter(empl => {
    return empl.employee_name.toLowerCase().includes(searchValue.toLowerCase());
  });

  const handleSearch = e => {
    updateSearch(e.target.value);
    updateEmployees(filteredEmpl);
  };

  return (
    <Container>
      <TopHeader>
        <div>
          Total employees: <strong>{employees.length}</strong> Filtered
          employees: <strong>{filteredEmpl.length}</strong>
        </div>
        <div>
          <input
            type="text"
            onChange={handleSearch}
            value={searchValue}
            placeholder="search"
          />
        </div>
      </TopHeader>

      <table style={{ width: "100%" }}>
        <thead>
          <tr>
            <th>id</th>
            <th>Employee name</th>
            <th>Employee salary</th>
            <th>Employee age</th>
          </tr>
        </thead>
        <tbody>
          {employees.map(employee => (
            <tr key={employee.id}>
              <td>{employee.id}</td>
              <td>{employee.employee_name}</td>
              <td>{employee.employee_salary}</td>
              <td>{employee.employee_age}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </Container>
  );
}

export default App;

有什么想法吗?

3 个答案:

答案 0 :(得分:0)

问题在于搜索词在这里过时了

const handleSearch = e => {
    updateSearch(e.target.value);
    updateEmployees(filteredEmpl);
  };
呼叫updateEmployees

。每次进行搜索时,您还将替换从api调用中获得的结果。无需将搜索字词设置为陈述,而是这样做:

  const [searchResult, updateSearch] = useState([]);

  const filterEmpl = useCallback((searchTerm) => {
    return employees.filter(({employee_name}) => {
      return employee_name.toLowerCase().includes(searchTerm.toLowerCase());
    })
  }, [employees]);

  const handleSearch = useCallback(({target}) => {
    const filteredEmpl = filterEmpl(target.value)
    updateSearch(filteredEmpl);
  }, [filterEmpl]);

答案 1 :(得分:0)

您不需要将筛选出的员工存储到状态变量中。每次更新searchValueemployees(使用useMemo)时,您只需要从原始员工那里进行计算即可。

顺便说一句,最好像上面一样将标题管理成自己的效果。

const [employees, updateEmployees] = useState([]);
const [searchValue, updateSearch] = useState("");

useEffect(() => {
  fetch("http://dummy.restapiexample.com/api/v1/employees")
    .then(res => res.json())
    .then(result => updateEmployees(result.data));
}, []);

useEffect(() {
  document.title = !employees.length ? "Loading..." : `Total: ${employees.length} `
}, [employees]);

const filteredEmpl = useMemo(() => {
   if (!searchValue) return employees;

   return employees.filter(empl => 
       empl.employee_name.toLowerCase().includes(searchValue.toLowerCase())
   );
}, [employees, searchValue]);

const handleSearch = e => updateSearch(e.target.value);

如果要对一组员工进行排序,可以这样做

const filteredEmpl = useMemo(() => {
   const sortFn = (empl1, empl2) => {...};
   const filterFn = empl => 
     empl.employee_name.toLowerCase().includes(searchValue.toLowerCase());

   if (!searchValue) {
     return [...employees].sort(sortFn);
   } else {
     return employees.filter(filterFn).sort(sortFn);
   }
}, [employees, searchValue]);

如果排序条件可以由用户(使用输入)更新,则需要将排序条件存储到新的状态变量中。

答案 2 :(得分:0)

我通过更改几个变量名并对代码进行了一些调整,并添加了过滤器功能。我希望这有帮助。如果您在此问题上需要任何进一步的帮助,请告诉我。干杯!

import React, { useState, useEffect } from "react";
import styled from "styled-components";

import "./App.css";

const Container = styled.div`
  width: 1280px;
  max-width: 100%;
  margin: 0 auto;
  th {
    text-align: left;
    padding: 10px;
    background: #f5f5f5;
    cursor: pointer;
    :hover {
      background: #ddd;
    }
  }
  td {
    border-bottom: 1px solid #f5f5f5;
    padding: 5px;
  }
`;

const TopHeader = styled.div`
  display: flex;
  justify-content: space-between;
  padding: 20px;
  input {
    width: 400px;
    padding: 10px;
  }
`;

const Loading = styled.div`
  display: flex;
  text-align: 'center';
  padding: 20px;
  font-size: 2em;
  font-weight: 300;
`;

const App = () => {
    const [employees, setEmployees] = useState([]); // Change variable name from updateEmployees to setEmployees
    const [searchValue, setSearchValue] = useState(""); // changed variable name from updateSearch to setSearchValue
    const [employeesTotal, setEmployeesTotal] = useState(0); // Add a new state to handle intial employees total

    // Renamed employees variable to employeesTotal
    if (employeesTotal) {
        document.title = "Loading...";
    }

    useEffect(() => {
        fetch("http://dummy.restapiexample.com/api/v1/employees")
            .then(res => res.json())
            .then(result => {
                setEmployees(result.data);
                setEmployeesLength(result.data.length);
                document.title = `Total: ${result.data.length} `; // Why though?
            });
    }, []);

    const handleSearch = e => {
        setSearchValue(e.target.value);
    };

    const filterDocument = doc => {
        const employeeName = doc.employee_name.toLowerCase() || '';
        return employeeName.includes(searchValue.toLowerCase());
    };

    // Check if employees array contains data, if it does, display content, otherwise show loading
    return (
            employeesTotal ? (
                <Container>
                    <TopHeader>
                        <div>
                            Total employees: <strong>{employeesTotal}</strong> Filtered employees: <strong>{employees.length}</strong>
                        </div>
                        <div>
                            <input
                                type="text"
                                onChange={handleSearch}
                                value={searchValue}
                                placeholder="search"
                            />
                        </div>
                    </TopHeader>

                    <table style={{ width: "100%" }}>
                        <thead>
                            <tr>
                                <th>id</th>
                                <th>Employee name</th>
                                <th>Employee salary</th>
                                <th>Employee age</th>
                            </tr>
                        </thead>
                        <tbody>
                            {/** Add filterDocument to filter function on employee array before calling its map funtion */}
                            {employees.filter(filterDocument).map(employee => (
                                <tr key={employee.id}>
                                    <td>{employee.id}</td>
                                    <td>{employee.employee_name}</td>
                                    <td>{employee.employee_salary}</td>
                                    <td>{employee.employee_age}</td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </Container>
            ) : (
                    <Loading>Loading...</Loading>
                )
    );
}

export default App;