如何调用组件类render方法中的函数方法?出现 Uncaught TypeError: 无法读取属性“值” React 中的 null

发布于 2025-01-11 09:10:03 字数 7357 浏览 0 评论 0 原文

我正在使用组件和功能组件创建一个 Web 应用程序。我使用组件类创建了 RegsiterLogin,但我很难使用组件类创建 Searchbar,因此我使用函数组件创建了它。我很困惑我应该在哪里调用搜索功能?当函数本身只是一个函数组件时,它本身运行没有问题,并且我没有任何用于注册或登录的代码,所以我没有相信错误与函数本身有关。但是,当我添加 RegisterLogin 的代码并从渲染中调用该函数时,它似乎不起作用并返回为 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;

I am creating a web application using both component and function components. I created the Regsiter and Login using component class but I was struggling to create the Searchbar using component class so I created it using a function component. I'm confused as to where I'm meant to call the search function? The function itself has no issue running on its own when its just a function component and I don't have any of the code for the Register or Login so I don't believe the error has to do with the function itself. However when I add the code for Register and Login and call the function from render it doesn't seem to work and returns as null, is there anything I'm missing? Is it possible I'm supposed to be doing something with 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>
    );
  }
}

Search

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

四叶草在未来唯美盛开 2025-01-18 09:10:04

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

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

直到最近,React 都尝试重用事件对象,因此一旦同步代码运行完毕,它会将 e.target 设置为 null。因此,当超时结束时,e.target 为 null,因此 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);
};

有关更多信息,查看此页面

Assuming you're on react 16 or earlier, the problem is this line:

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

Up until recently, react tried to reuse event objects, so it would set e.target to null once synchronous code is done running. As a result, by the time the timeout goes off, e.target is null, so e.target.value throws an exception.

Option 1: save the value in a local variable and use that

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

Option 2: tell react to not reuse this event object

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

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

For more information, see this page

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文