React中结合createContext与useState实现分类CRUD的正确实践求助
Hey there! Let's fix up your Context-based CRUD setup step by step—you're on the right track, just a few key mistakes to iron out.
Key Issues in Your Current Code
- Wrong hook for reducer logic: You're using
useStateto work with yourCategoriaReducer, but React has a dedicated hook for this:useReducer.useStateis for simple state updates, whileuseReduceris designed for state logic that involves multiple actions/reducers. - Missing methods in Context: Your
CategoriaContext.Provideronly passescategoriasfrom state, but not theGetCategoriasmethod. That's why when you try to callGetCategorias()inAllCat, it throws an error (it can't find the function). - Initial state setup: The way you initialized state with
useState(CategoriaReducer, initialState)is incorrect—useReducertakes the reducer and initial state as arguments, notuseState.
Fixed Component Code
1. categoriacontex.js (no changes needed, just confirming)
import { createContext } from "react"; const CategoriaContext = createContext(); export default CategoriaContext;
2. types.js (expanded for full CRUD actions)
export const GET_CATEGORIAS = "GET_CATEGORIAS"; export const ADD_CATEGORIA = "ADD_CATEGORIA"; export const DELETE_CATEGORIA = "DELETE_CATEGORIA"; export const UPDATE_CATEGORIA = "UPDATE_CATEGORIA"; export const SELECT_CATEGORIA = "SELECT_CATEGORIA";
3. categoriasreducer.js (updated to handle all CRUD actions)
import { GET_CATEGORIAS, ADD_CATEGORIA, DELETE_CATEGORIA, UPDATE_CATEGORIA, SELECT_CATEGORIA } from "../types"; export default (state, action) => { const { payload, type } = action; switch (type) { case GET_CATEGORIAS: return { ...state, categorias: payload, }; case ADD_CATEGORIA: return { ...state, categorias: [...state.categorias, payload], }; case DELETE_CATEGORIA: return { ...state, categorias: state.categorias.filter(cat => cat.id !== payload), }; case UPDATE_CATEGORIA: return { ...state, categorias: state.categorias.map(cat => cat.id === payload.id ? payload : cat ), selectedCategoria: null, // Reset selected after update }; case SELECT_CATEGORIA: return { ...state, selectedCategoria: payload, }; default: return state; } };
4. categoriastate.js (major fix: use useReducer and expose all methods)
import React, { useReducer, useEffect } from 'react'; import CategoriaContext from './CategoriaContext'; import CategoriaReducer from './CategoriasReducer'; import Data from '../../Data/Categorias.json'; import { GET_CATEGORIAS, ADD_CATEGORIA, DELETE_CATEGORIA, UPDATE_CATEGORIA, SELECT_CATEGORIA } from "../types"; const CategoriaState = (props) => { const initialState = { categorias: [], selectedCategoria: null, }; // Use useReducer instead of useState for reducer-based state management const [state, dispatch] = useReducer(CategoriaReducer, initialState); // Load initial categories when the context mounts useEffect(() => { GetCategorias(); }, []); // Fetch all categories from JSON const GetCategorias = () => { try { dispatch({ type: GET_CATEGORIAS, payload: Data }); } catch (error) { console.error(error); } }; // Add a new category const AddCategoria = (categoria) => { const newCat = { id: Date.now(), // Simple unique ID for demo (adjust as needed) ...categoria, }; dispatch({ type: ADD_CATEGORIA, payload: newCat }); }; // Delete a category by ID const DeleteCategoria = (id) => { dispatch({ type: DELETE_CATEGORIA, payload: id }); }; // Select a category for editing const SelectCategoria = (categoria) => { dispatch({ type: SELECT_CATEGORIA, payload: categoria }); }; // Update an existing category const UpdateCategoria = (categoria) => { dispatch({ type: UPDATE_CATEGORIA, payload: categoria }); }; return ( <CategoriaContext.Provider value={{ categorias: state.categorias, selectedCategoria: state.selectedCategoria, GetCategorias, AddCategoria, DeleteCategoria, SelectCategoria, UpdateCategoria }} > {props.children} </CategoriaContext.Provider> ); }; export default CategoriaState;
5. allcat.js (updated to display categories and add CRUD controls)
import React from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; import { useContext } from 'react'; import CategoriaContext from '../../Context/Categorias/CategoriaContext'; import { Link } from 'react-router-dom'; const AllCat = () => { const { categorias, DeleteCategoria, SelectCategoria } = useContext(CategoriaContext); return ( <div className="container mx-auto mt-4"> <div className="d-flex justify-content-between align-items-center mb-3"> <h2>Categorias</h2> <Link to="/addcat" className="btn btn-primary">Add New Category</Link> </div> <div className="card col-md-8 mx-auto"> <div className="card-body px-0"> {categorias.length === 0 ? ( <p className="text-center py-3">No categories found.</p> ) : ( <ul className="list-group list-group-flush"> {categorias.map(cat => ( <li key={cat.id} className="list-group-item d-flex justify-content-between align-items-center"> <div> <h5>{cat.description}</h5> {cat.idfather && <small className="text-muted">Parent ID: {cat.idfather}</small>} </div> <div className="btn-group"> <Link to="/addcat" className="btn btn-sm btn-outline-secondary" onClick={() => SelectCategoria(cat)} > Edit </Link> <button className="btn btn-sm btn-outline-danger" onClick={() => DeleteCategoria(cat.id)} > Delete </button> </div> </li> ))} </ul> )} </div> </div> </div> ); }; export default AllCat;
6. AddCat.js (example component for adding/editing categories)
import React, { useState, useEffect, useContext } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; import CategoriaContext from '../../Context/Categorias/CategoriaContext'; import { useHistory } from 'react-router-dom'; const AddCat = () => { const { selectedCategoria, AddCategoria, UpdateCategoria } = useContext(CategoriaContext); const history = useHistory(); const [formData, setFormData] = useState({ description: '', idfather: '' }); // Populate form if editing an existing category useEffect(() => { if (selectedCategoria) { setFormData(selectedCategoria); } else { setFormData({ description: '', idfather: '' }); } }, [selectedCategoria]); const handleChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; const handleSubmit = (e) => { e.preventDefault(); if (selectedCategoria) { UpdateCategoria(formData); } else { AddCategoria(formData); } history.push('/allcat'); }; return ( <div className="container mx-auto mt-4"> <div className="card col-md-6 mx-auto"> <h4 className="card-title text-center py-3 border-bottom"> {selectedCategoria ? 'Edit Category' : 'Add New Category'} </h4> <div className="card-body"> <form onSubmit={handleSubmit}> <div className="mb-3"> <label className="form-label">Description</label> <input type="text" className="form-control" name="description" value={formData.description} onChange={handleChange} required /> </div> <div className="mb-3"> <label className="form-label">Parent ID (optional)</label> <input type="number" className="form-control" name="idfather" value={formData.idfather} onChange={handleChange} /> </div> <div className="d-flex justify-content-between"> <button type="button" className="btn btn-secondary" onClick={() => history.push('/allcat')}> Cancel </button> <button type="submit" className="btn btn-primary"> {selectedCategoria ? 'Update' : 'Add'} </button> </div> </form> </div> </div> </div> ); }; export default AddCat;
Key Change Explanations
useReducerinstead ofuseState: This hook is built for state logic that uses reducers—dispatchsends actions to the reducer, which updates state based on the action type, keeping your state management organized.- Full Context exposure: The Provider now passes all CRUD methods and state values, so components can access everything they need without errors.
- Auto-load initial data: We added
useEffectinCategoriaStateto load your JSON data when the context mounts, so you don't have to callGetCategoriasin every component. - Complete CRUD flow: Added actions, reducer cases, and UI components for adding, editing, deleting, and viewing categories.
JSON Data Structure Note
Ensure your Categorias.json matches this format to work with the code:
[ { "id": 1, "description": "Electronics", "idfather": null }, { "id": 2, "description": "Laptops", "idfather": 1 } ]
That should get your CRUD app working smoothly! Let me know if you hit any other snags.
内容的提问来源于stack exchange,提问作者user15242898
相关产品推荐
相关产品推荐

