简体   繁体   English

如何在反应中正确地有条件地渲染子组件

[英]How to properly conditionally render child component in react

I have the following components: (AddBookPanel contains the AddBookForm)我有以下组件:(AddBookPanel 包含 AddBookForm)

I need to have the ability to display the form on click of the 'AddBookButton', and also have the ability to hide the form while clicking the 'x' button (img in AddBookForm component), however the component doesn't seem to load properly, what could be wrong here?我需要能够在单击“AddBookButton”时显示表单,并且还能够在单击“x”按钮(AddBookForm 组件中的 img)时隐藏表单,但是该组件似乎无法加载正确的,这里可能有什么问题? What do you think is the best way to organize this?你认为最好的组织方式是什么?

import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';

export const AddBookPanel = () => {
  const [addBookPressed, setAddBookPressed] = useState(false);

  return (
    <div>
      <div onClick={() => setAddBookPressed(!addBookPressed)}>
        <AddBookButton />
      </div>
      <AddBookForm initialFormActive={addBookPressed} />
    </div>
  );
};
import { useState } from 'react';

interface AddBookFormProps {
  initialFormActive: boolean;
}

export const AddBookForm: React.FC<AddBookFormProps> = ({
  initialFormActive,
}) => {
  const [isFormActive, setIsFormActive] = useState(initialFormActive);

  return (
    <>
      {isFormActive && (
        <div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
          <div className="flex justify-between bg-slate-400">
            <div>
              <p className="text-4xl my-5 mx-6">Add a new Book</p>
            </div>
            <div className="h-16 w-16 my-3 mx-3">
              <button onClick={() => setIsFormActive(!isFormActive)}>
                <img
                  src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
                  alt="close-button"
                  className="object-fit"
                ></img>
              </button>
            </div>
          </div>

          <div className="flex-col">
            <div className="flex justify-between my-10 ml-5 text-xl">
              <input type="text" placeholder="book name"></input>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

You're always rendering the AddBookForm .您总是在渲染AddBookForm That means in the initial render the useState is called with false .这意味着在初始渲染中使用false调用useState Since you never call setIsFormActive it is now stuck as "do not render".由于您从不调用setIsFormActive它现在被困为“不渲染”。

Just don't use the useState and use the property initialFormActive directly.只是不要使用useState并直接使用属性initialFormActive And maybe rename it to isFormActive .也许将其重命名为isFormActive Let the parent handle the state change.让父处理 state 更改。

import { AddBookButton } from './AddBookButton';
import { AddBookForm } from './AddBookForm';
import { useState } from 'react';

export const AddBookPanel = () => {
  const [addBookPressed, setAddBookPressed] = useState(false);

  return (
    <div>
      <div onClick={() => setAddBookPressed(!addBookPressed)}>
        <AddBookButton />
      </div>
      <AddBookForm initialFormActive={addBookPressed} onClose={() => setAddBookPressed(false)} />
    </div>
  );
};
import { useState } from 'react';

interface AddBookFormProps {
  initialFormActive: boolean;
  onColse: () => void;
}

export const AddBookForm: React.FC<AddBookFormProps> = ({
  initialFormActive,
  onClose,
}) => {
  // const [isFormActive, setIsFormActive] = useState(initialFormActive); don't use this frozen state, allow the parent to control visibility

  return (
    <>
      {initialFormActive && (
        <div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
          <div className="flex justify-between bg-slate-400">
            <div>
              <p className="text-4xl my-5 mx-6">Add a new Book</p>
            </div>
            <div className="h-16 w-16 my-3 mx-3">
              <button onClick={() => onClose()}>
                <img
                  src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
                  alt="close-button"
                  className="object-fit"
                ></img>
              </button>
            </div>
          </div>

          <div className="flex-col">
            <div className="flex justify-between my-10 ml-5 text-xl">
              <input type="text" placeholder="book name"></input>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

Problem with your code is in how you tied initialFormActive to inner state of AddBookForm component.您的代码的问题在于您如何将initialFormActiveAddBookForm组件的内部 state 。 When looking at useState inside AddBookForm it is clear that prop initialFormActive only affects your initial state, and when looking at your parent component I can clearly see that you passed false as value for initialFormActive prop, thus initializing state inside AddBookForm with false value(form will stay hidden).当查看 AddBookForm 中的AddBookForm时,很明显 prop initialFormActive只会影响您的初始 state,当查看您的父组件时,我可以清楚地看到您将 false 作为initialFormActive道具的值,因此在AddBookForm中使用 false 值初始化 state(表单将保持隐藏)。 Then you expect when you click on button, and when prop value changes(becomes true), that your <AddBookForm /> will become visible, but it will not because you just used initial value(false) from prop and after that completely decoupled from prop value change.然后,您期望当您单击按钮时,并且当道具值更改(变为真)时,您的<AddBookForm />将变得可见,但这不会因为您只是使用道具的初始值(假),然后完全脱离道具价值变化。

Since you want to control visibility outside of component, then you should attach two props to AddBookForm, isVisible and toggleVisibiliy .由于您想控制组件外部的可见性,因此您应该将两个道具附加到 AddBookForm, isVisibletoggleVisibiliy And do something like this:并做这样的事情:

 export const AddBookPanel = () => {
  const [addBookPressed, setAddBookPressed] = useState(false);
  const toggleAddBookPressed = () => setAddBookPressed(p => !p);

  return (
    <div>
      <div onClick={() => setAddBookPressed(!addBookPressed)}>
        <AddBookButton />
      </div>
      <AddBookForm isVisible={addBookPressed} toggleVisibility={toggleAddBookPressed } />
    </div>
  );

}; };

export const AddBookForm: React.FC<AddBookFormProps> = ({
  isVisible, toggleVisibility
}) => {

  return (
    <>
      {isVisible&& (
        <div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
          <div className="flex justify-between bg-slate-400">
            <div>
              <p className="text-4xl my-5 mx-6">Add a new Book</p>
            </div>
            <div className="h-16 w-16 my-3 mx-3">
              <button onClick={toggleVisibility}>
                <img
                  src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
                  alt="close-button"
                  className="object-fit"
                ></img>
              </button>
            </div>
          </div>

          <div className="flex-col">
            <div className="flex justify-between my-10 ml-5 text-xl">
              <input type="text" placeholder="book name"></input>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

Try this one试试这个

import { useState } from 'react';

interface AddBookFormProps {
  initialFormActive: boolean;
}

export const AddBookForm: React.FC<AddBookFormProps> = ({
  initialFormActive,
}) => {
  const [isFormActive, setIsFormActive] = useState(initialFormActive);

  return (
    <>
      {isFormActive ? (
        <div className="fixed box-border h-2/3 w-1/3 border-4 left-1/3 top-24 bg-white border-slate-600 shadow-xl">
          <div className="flex justify-between bg-slate-400">
            <div>
              <p className="text-4xl my-5 mx-6">Add a new Book</p>
            </div>
            <div className="h-16 w-16 my-3 mx-3">
              <button onClick={() => setIsFormActive(!isFormActive)}>
                <img
                  src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/OOjs_UI_icon_close.svg/1200px-OOjs_UI_icon_close.svg.png"
                  alt="close-button"
                  className="object-fit"
                ></img>
              </button>
            </div>
          </div>

          <div className="flex-col">
            <div className="flex justify-between my-10 ml-5 text-xl">
              <input type="text" placeholder="book name"></input>
            </div>
          </div>
        </div>
      ):(<><h1>Solution when it is false</h1></>)}
    </>
  );
};

You need to add your JSX for true condition.您需要添加您的 JSX 以获得true条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM