所以我想简化将props 传递给父组件的孙子组件,其中更改按钮或通道的打开或关闭状态(如:[open, setOpen]= useState()
)可以简化为useReducer,并从父组件传递.无论如何,我不能理解为什么我它不工作,也许我理解错了逻辑.理想情况下,我希望接近枯燥的本金,并尽可能简单地传递props .
错误:
AddButtonModal.tsx:36 Warning: Failed prop type: The prop
Open is marked as required in
ForwardRef(模数2), but its value is
未定义.
父零部件:
import React, {useReducer, useState} from "react";
import {Ingredient} from "../../models/ingredient";
import {Box} from "@mui/material";
// Importing the 4 components for their respective part that way we have a more concise Page
import AddButtonModal from "./components/IngredientsManagementViewComponents/AddButtonModal";
import IngredientCategoryMenu from "./components/IngredientsManagementViewComponents/IngredeintCategoryMenu";
import SearchBarComponent from "./components/IngredientsManagementViewComponents/SearchBar";
import ingredientReducer, {IngredientActionTypes, initialState} from "./reducers/ingredientReducer";
import modalReducer, {ModalActionTypes, initialModalState} from "./reducers/modalReducer";
import ListOfIngredients from "./components/IngredientsManagementViewComponents/ListOfIngredients";
const IngredientsViewPage: React.FC = () => {
//states
//state of the new ingredient
const [ingredientState, dispatch] = useReducer(ingredientReducer, initialState);
console.log("ingredientState:", ingredientState);
// state for the open/close function for modals/buttons
const [modalState, modalDispatch] = useReducer(modalReducer, initialModalState);
console.log("modalState: within IVP", modalState);
console.log("initialModalState within IVP: ", initialModalState);
const handleOpenModal = () => {
console.log("Open Modal");
modalDispatch({ type: ModalActionTypes.OPEN_MODAL}) };
const handleCloseModal = () => {
console.log("Close Modal");
modalDispatch({type: ModalActionTypes.CLOSE_MODAL}) };
/*An object refering to the Ingredient model properties which can later be called to map each Number Input */
const IngredientFieldsOptions = {
carbs: {label: "Carbohydrates", value: ingredientState.carbs},
protein: {label: "Protein", value: ingredientState.protein},
sugar: {label: "Sugar", value: ingredientState.sugar},
fat:{label: "Fat", value: ingredientState.fat},
fiber: {label: "Fiber", value: ingredientState.fiber}
}
return (
<Box sx={{ display: "flex", flexDirection: "column",
bgcolor: "whitesmoke", borderRadius: "3px", borderBlockColor: "none"}}>
{/*Search field with search lens*/}
<Box sx={{display: "flex", flexDirection: "row", width: "100%"}}>
<SearchBarComponent/>
{/*Button with the + symbol, opens a modal */}
<AddButtonModal
// IngredientFieldsOptions={IngredientFieldsOptions}
isModalOpen={modalState.isModalOpen}
handleOpenModal={handleOpenModal}
handleCloseModal={handleCloseModal}
/>
</Box>
{/* Component with the Categories menu */}
<IngredientCategoryMenu/>
{/* Component List of Ingredients */}
<ListOfIngredients
open={modalState.isModalOpen}
handleOpenModal={handleOpenModal}
handleCloseModal={handleCloseModal}
ingredientState={ingredientState}
dispatch={dispatch}
/>
</Box>
)
};
export default IngredientsViewPage;
子元素:
import React, {useReducer, useState} from "react";
import {Box, Button, Modal, Typography} from "@mui/material";
import Add from "@mui/icons-material/Add";
import ingredientReducer, {IngredientActionTypes, initialState} from "../../reducers/ingredientReducer.ts";
import {Ingredient} from "../../../../models/ingredient.ts";
import XButton from "./XButton.tsx";
import IngredientFields from "./IngredientFields.tsx";
import DropDowns from "./DropDown.tsx";
import CheckBoxSection from "./CheckBoxSection.tsx";
import NumberInputFields from "./NumberInputFields.tsx";
import SaveButton from "./SaveButton.tsx";
import { dummy_allergens } from "../../../../dummy_data/dummy_allergens.ts";
import { dummy_ingredients } from "../../../../dummy_data/dummy_ingredients.ts";
import { number } from "yup";
//defined types of the props
interface AddButtonModalProps {
isModalOpen: boolean;
handleOpenModal: () => void;
handleCloseModal: () => void;
}
const AddButtonModal: React.FC<AddButtonModalProps> = ({isModalOpen, handleOpenModal, handleCloseModal}) => {
const [ingredientState, dispatch] = useReducer(ingredientReducer, initialState);
console.log("modalState: within ABM", isModalOpen);
return (
<Box sx={{marginBottom: 2, }}>
<Button onClick={handleOpenModal} sx={{
minWidth: 100,
height: "auto",
display: "flex",
}}
startIcon={<Add />}
id="Container-search bar & + button">
Add new
<Modal
open={isModalOpen}
onClose={handleCloseModal}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "70vw",
bgcolor: "whitesmoke",
boxShadow: 24,
borderRadius: "3px",
p: 4,
overflowY: "auto", // Scroll for overflow content
maxHeight: "90vh",
}} id="main container"
onClick={(event) => event.stopPropagation()}
>
{/* Label & X icon button */}
<Box sx={{display: "flex", flexDirection: "row", justifyContent: "space-between", alignItems: "center"}}
id="container-title & close-icon">
<Typography id="modal-modal-title" variant="h2" component="h2" sx={{fontSize: "1.2rem"}}>
ADD INGREDIENT
</Typography>
<XButton handleCloseModal={handleCloseModal}/>
</Box>
{/* Ingredients Fields Components*/}
// name of the ingredient
<IngredientFields />
{/* Dropdowns components */}
<DropDowns />
{/* Checkboxes components */}
<CheckBoxSection/>
{/* Number Input */}
<NumberInputFields
ingredientState={ingredientState}
dispatch={dispatch}
// IngredientFieldsOptions={IngredientFieldsOptions}
/>
{/* Save Button */}
<SaveButton handleCloseModal={handleCloseModal}/>
</Box>
</Modal>
</Button>
</Box>
);
};
export default AddButtonModal;
孙子:
import React from "react";
import {Box, IconButton} from "@mui/material";
import ClearIcon from '@mui/icons-material/Clear';
//defined types of the props
interface XButtonProps {
handleCloseModal: () => void;
}
const XButton: React.FC<XButtonProps> = ({handleCloseModal}) => {
return (
<Box>
{/* Icon that closes the modal */}
<IconButton onClick={handleCloseModal} style={{ cursor: 'pointer' }}>
<ClearIcon />
</IconButton>
</Box>
);
};
export default XButton;
后退.后退.
// Define action types related to modal operation
export enum ModalActionTypes {
OPEN_MODAL = 'OPEN_MODAL',
CLOSE_MODAL = 'CLOSE_MODAL'
}
// set opening action
type OpenModalAction = {
type: ModalActionTypes.OPEN_MODAL
};
// set closing action
type CloseModalAction = {
type: ModalActionTypes.CLOSE_MODAL
}
// Union type for modal-related actions
type ModalAction = OpenModalAction | CloseModalAction;
// initial state of the modal
export const initialModalState = {
isModalOpen: false
};
/** reducer function for handling of modal state changes
@params {object} state: current state
@param {ModalAction} action: action to be handled
@returns {object} updated state of modal
*/
function modalReducer(state = initialModalState, action: ModalAction) {
switch (action.type) {
case ModalActionTypes.OPEN_MODAL:
return {...state, isModalOpen: true};
case ModalActionTypes.CLOSE_MODAL:
return {...state, isModalOpen: false};
default:
return state;
// throw Error("Unknown action: " + action.type);
}
}
export default modalReducer;
我try 使用sole.log查看每个组件中的状态: 父零部件将显示:
modalState: within IVP , isModalOpen: false line 22,
modalState: within IVP , isModalOpen: false line 23,
点击应该打开模式的按钮后,我可以在控制台中看到
OpenModal ln 25,
modalState: within IVP , isModalOpen: true line 22,
modalState: within IVP , isModalOpen: false line 23.
我也try 了硬编码:父组件中第49行的isModalOpen={modalState.isModalOpen}
.我可以分辨出,父母确实有这种状态,但不会被传递.
此外,我读了这篇文章,但我不确定这是否真的有帮助. Warning: Failed prop type: The prop open is marked as required in Snackbar, but its value is undefined个