Search field kicks me out on input field after 1 letter

Viewed 53

this is in React. I have a search input field, however after typing one letter it keeps me out of the input field and renders the page again. The search field does work, it just kicks me out. I've tried adding a

onChange={(e) => setSearchField(e.target.value), function(e) {
  e.preventDefault();
}}

to the input field but it doesn't work. Here's my whole file:

import React, { useState, useEffect } from "react";
import { Container, Row, Col, Input } from "reactstrap";
import MeetingTable from "./MeetingTable";
import MeetingCreate from "./MeetingCreate";
import MeetingEdit from "./MeetingEdit";
import APIURL from "../helpers/environment";
import styled from "styled-components";
import "./MeetingMain.css";

const MeetingMain = (props) => {
  const Div = styled.div`
    background-color: #363136;
    opacity: 0.8;
    border-radius: 5px;
    padding-top: 10px;
    padding-left: 10px;
  `;

  const [meetings, setMeetings] = useState([]);
  const [updateActive, setUpdateActive] = useState(false);
  const [meetingToUpdate, setMeetingToUpdate] = useState({});
  const [searchField, setSearchField] = useState("");

  const tableStyle = {
    display: "flex",
    flexDirection: "column",
    justifyContent: "center",
    alignItems: "center",
    maxWidth: "1175px",
  };

  const fetchMeetings = () => {
    fetch(`${APIURL}/meeting`, {
      method: "GET",
      headers: new Headers({
        "Content-Type": "application/json",
        Authorization: props.token,
      }),
    })
      .then((res) => res.json())
      .then((logData) => {
        setMeetings(logData.meetings);
        console.log(logData.meetings);
      });
  };

  const editUpdateMeeting = (meeting) => {
    setMeetingToUpdate(meeting);
    console.log(meeting);
  };

  const updateOn = () => {
    setUpdateActive(true);
  };

  const updateOff = () => {
    setUpdateActive(false);
  };

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

  const filteredMeetings = meetings.filter((meeting) =>
    meeting.day.toLowerCase().includes(searchField.toLowerCase())
  );

  return (
    <Div>
      <Container style={tableStyle}>
        <Row>
          <Col md="12">
            <MeetingCreate fetchMeetings={fetchMeetings} token={props.token} />
          </Col>
          <Col md="12">
            <Input
              className="search-field"
              type="search"
              placeholder="Search Meetings"
              onChange={(e) => setSearchField(e.target.value)}
              value={searchField}
            />
            <MeetingTable
              meetings={filteredMeetings}
              editUpdateMeeting={editUpdateMeeting}
              updateOn={updateOn}
              fetchMeetings={fetchMeetings}
              token={props.token}
            />
          </Col>
          {updateActive ? (
            <MeetingEdit
              meetingToUpdate={meetingToUpdate}
              updateOff={updateOff}
              token={props.token}
              fetchMeetings={fetchMeetings}
            />
          ) : (
            <></>
          )}
        </Row>
      </Container>
    </Div>
  );
};

export default MeetingMain;

So I'm a bit at a loss on what's causing this. Any help would be appreciated.

2 Answers

Issue

You're defining a styled component inside your functional component, this means it's a new component each render cycle. In other words, it is a new component and mounted & rendered versus just being rerendered when state updates from the onChange handler.

Define Styled Components outside of the render method

It is important to define your styled components outside of the render method, otherwise it will be recreated on every single render pass. Defining a styled component within the render method will thwart caching and drastically slow down rendering speed, and should be avoided.

Recall: The entire body of a functional component IS the render "method".

Solution

Declare the Div component outside MeetingMain so it is a stable component reference.

const Div = styled.div`
  background-color: #363136;
  opacity: 0.8;
  border-radius: 5px;
  padding-top: 10px;
  padding-left: 10px;
`;

const MeetingMain = (props) => {
  const [meetings, setMeetings] = useState([]);
  const [updateActive, setUpdateActive] = useState(false);
  const [meetingToUpdate, setMeetingToUpdate] = useState({});
  const [searchField, setSearchField] = useState("");

You should move Div outside of your MeetingMain component as below.

const Div = styled.div`
    background-color: #363136;
    opacity: 0.8;
    border-radius: 5px;
    padding-top: 10px;
    padding-left: 10px;
  `;

const MeetingMain = (props) => {
    ...
}

Check it out here

Related