How to call a function method in a component class render method? getting Uncaught TypeError: Cannot read property 'value' of null in React

How to call a function method in a component class render method? getting Uncaught TypeError: Cannot read property 'value' of null in React

我正在创建一个同时使用组件和功能组件的 Web 应用程序。我使用组件 class 创建了 RegsiterLogin,但我正在努力使用组件 class 创建 Searchbar,所以我使用函数组件创建了它。我对我应该在哪里调用搜索功能感到困惑?当函数本身只是一个函数组件时,它本身没有问题 运行,而且我没有 RegisterLogin 的任何代码,所以我不相信错误与函数本身有关。但是,当我为 RegisterLogin 添加代码并从 render 调用该函数时,它似乎不起作用并且 returns 为 null,我是否遗漏了什么?有没有可能我应该用 componentDidMount() 做点什么?

import React, { Component, useState } from "react";
import axios from 'axios';
import { Switch, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import styled from "styled-components";

import AuthService from "./services/auth.service";

import Login from "./components/login.component";
import Register from "./components/register.component";
import Home from "./components/home.component";
import Profile from "./components/profile.component";
import BoardUser from "./components/board-user.component";
import BoardCreator from "./components/board-creator.component";
import BoardAdmin from "./components/board-admin.component";
import MovieList from "./components/movie-list.component";

// import AuthVerify from "./common/auth-verify";
import EventBus from "./common/EventBus";


const Container = styled.div`
  display: flex;
  flex-direction: column;
`;
const AppName = styled.div`
  display: flex;
  flex-direction: row;
  align-items: center;
`;
const Header = styled.div`
  background-color: black;
  color: white;
  display: flex;
  justify-content: space-between;
  flex-direction: row;
  align-items: center;
  padding: 10px;
  font-size: 25px;
  font-weight: bold;
  box-shadow: 0 3px 6px 0 #555;
`;
const SearchBox = styled.div`
  display: flex;
  flex-direction: row;
  padding: 10px 10px;
  border-radius: 6px;
  margin-left: 20px;
  width: 50%;
  background-color: white;
`;

const SearchInput = styled.input`
  color: black;
  font-size: 16px;
  font-weight: bold;
  border: none;
  outline: none;
  margin-left: 15px;
`;

const MovieListContainer = styled.div`
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  padding: 30px;
  gap: 25px;
  justify-content: space-evenly;;
`;

class App extends Component {
  constructor(props) {
    super(props);
    this.logOut = this.logOut.bind(this);

    this.state = {
      showCreatorBoard: false,
      showAdminBoard: false,
      currentUser: undefined,
      timeoutId: "",
      updateTimeoutId: ""
    };
}

  componentDidMount() {
    const user = AuthService.getCurrentUser();

    if (user) {
      this.setState({
        currentUser: user,
        showCreatorBoard: user.roles.includes("ROLE_MODERATOR"),
        showAdminBoard: user.roles.includes("ROLE_ADMIN"),
      });
    }
    
    EventBus.on("logout", () => {
      this.logOut();
    });

    
  }

  componentWillUnmount() {
    EventBus.remove("logout");
  }

  logOut() {
    AuthService.logout();
    this.setState({
      showCreatorBoard: false,
      showAdminBoard: false,
      currentUser: undefined,
    });
  }

  render() {
    const { currentUser, showCreatorBoard, showAdminBoard} = this.state;

    return (
      <div>
        <nav className="navbar navbar-expand navbar-dark bg-dark">
          <Link to={"/"} className="navbar-brand">
            Movie App
          </Link>
          <div className="navbar-nav mr-auto">
            <li className="nav-item">
              <Link to={"/home"} className="nav-link">
                Home
              </Link>
            </li>

            {showCreatorBoard && (
              <li className="nav-item">
                <Link to={"/mod"} className="nav-link">
                  Moderator Board
                </Link>
              </li>
            )}

            {showAdminBoard && (
              <li className="nav-item">
                <Link to={"/admin"} className="nav-link">
                  Admin Board
                </Link>
              </li>
            )}

            {currentUser && (
              <li className="nav-item">
                <Link to={"/user"} className="nav-link">
                  User
                </Link>              
              </li>
            )}
          </div>         
          <Search />
          

          {currentUser ? (
            <div className="navbar-nav ml-auto">
              <li className="nav-item">
                <Link to={"/profile"} className="nav-link">
                  {currentUser.username}
                </Link>
              </li>
              <li className="nav-item">
                <a href="/login" className="nav-link" onClick={this.logOut}>
                  LogOut
                </a>
              </li>
            </div>
          ) : (
            <div className="navbar-nav ml-auto">
              <li className="nav-item">
                <Link to={"/login"} className="nav-link">
                  Login
                </Link>
              </li>

              <li className="nav-item">
                <Link to={"/register"} className="nav-link">
                  Sign Up
                </Link>
              </li>
            </div>
          )}
        </nav>

        <div className="container mt-3">
          <Switch>
            <Route exact path={["/", "/home"]} component={Home} />
            <Route exact path="/login" component={Login} />
            <Route exact path="/register" component={Register} />
            <Route exact path="/profile" component={Profile} />
            <Route path="/films" 
                  component={MovieList}
                  
                  /> 
            <Route path="/user" component={BoardUser} />
            <Route path="/creator" component={BoardCreator} />
            <Route path="/admin" component={BoardAdmin} />
          </Switch>
        </div>
        <MovieListContainer>
          <MovieList></MovieList>
          <MovieList></MovieList>
          <MovieList></MovieList>
          <MovieList></MovieList>
        </MovieListContainer>

        { /*<AuthVerify logOut={this.logOut}/> */ }
      </div>
    );
  }
}

搜索

function Search() {
  const [searchQuery, setSearchQuery] = useState(""); 
  const [timeoutId, updateTimeoutId] = useState();

  const fetchData = async (searchString) => {
    const response = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=f134dfeac1ebb17feefa58d7f94e94cd&language=en-US&query=${searchString}&page=1&include_adult=false`);
    console.log(response);
  };

  const onTextChange = (e) => {
    clearTimeout(timeoutId);
    setSearchQuery(e.target.value);
    const timeout = setTimeout(() => fetchData(e.target.value), 500);
    updateTimeoutId(timeout);
  };

  return(
    <div> 
      <SearchBox>
      <SearchInput placeholder="SEARCH" value={searchQuery} onChange={onTextChange}></SearchInput>
      </SearchBox></div>
  );
}

export default App;

假设您使用的是 React 16 或更早版本,问题出在这一行:

const timeout = setTimeout(() => fetchData(e.target.value), 500);

直到最近,React 还试图重用事件对象,因此一旦同步代码完成 运行,它就会将 e.target 设置为 null。结果,到超时结束时,e.target 为空,因此 e.target.value 抛出异常。

选项 1:将值保存在局部变量中并使用它

const onTextChange = (e) => {
  clearTimeout(timeoutId);
  const value = e.target.value;
  setSearchQuery(value);
  const timeout = setTimeout(() => fetchData(value), 500);
  updateTimeoutId(timeout);
};

选项 2:告诉 React 不要重用这个事件对象

const onTextChange = (e) => {
  e.persist();

  clearTimeout(timeoutId);
  setSearchQuery(e.target.value);
  const timeout = setTimeout(() => fetchData(e.target.value), 500);
  updateTimeoutId(timeout);
};

更多信息,see this page