You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 useState to work with your CategoriaReducer, but React has a dedicated hook for this: useReducer. useState is for simple state updates, while useReducer is designed for state logic that involves multiple actions/reducers.
  • Missing methods in Context: Your CategoriaContext.Provider only passes categorias from state, but not the GetCategorias method. That's why when you try to call GetCategorias() in AllCat, it throws an error (it can't find the function).
  • Initial state setup: The way you initialized state with useState(CategoriaReducer, initialState) is incorrect—useReducer takes the reducer and initial state as arguments, not useState.

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

  1. useReducer instead of useState: This hook is built for state logic that uses reducers—dispatch sends actions to the reducer, which updates state based on the action type, keeping your state management organized.
  2. Full Context exposure: The Provider now passes all CRUD methods and state values, so components can access everything they need without errors.
  3. Auto-load initial data: We added useEffect in CategoriaState to load your JSON data when the context mounts, so you don't have to call GetCategorias in every component.
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 15:02:47