当前位置: 首页 > 知识库问答 >
问题:

如何在React js中使用上下文API管理全局状态

高海阳
2023-03-14

我在使用useContext管理导航栏的状态时遇到问题。Atm我的应用程序在菜单切换后立即呈现菜单项。我希望此事件仅在单击一次时发生,并且按钮不记录console.log消息,它仅在我直接单击链接项ex:home时起作用。所以我有两个问题。如何管理导航栏状态以显示如何隐藏菜单项,而不必为其创建新组件?如何修复在菜单按钮本身或/和菜单项上触发的单击事件?下面您将为App.js、Layout.js、ThemeContext.js、useTheme.js、usetokle.js、ToggleContext.js和使用切换上下文的导航栏编写代码片段。

我真的很想在这里得到一些帮助,伙计们,我是低年级学生,真的有点被困在这里了。提前感谢大家。

狮子座

一个pp.js

//import { data } from '../../SkillData';
import Header from './Header';
import Navbar from './Navbar';
import Skills from './Skills';
import Layout from './Layout';

function App () {

    return (
        <Layout startingTheme="light" startingToggle={"show"}>
        <div>
        <Navbar />
        <Header />
        <Skills />
        </div>
        </Layout>
    );
}

export default App;

我ayout.js

import React, { useContext } from "react";
import { ThemeContext, ThemeProvider } from "../contexts/ThemeContext";
import { ToggleContext, ToggleProvider } from "../contexts/ToggleContext";
function Layout ({startingTheme, startingToggle, children}) { 
    return (
        <>
        <ThemeProvider startingTheme={startingTheme} >
            <ToggleProvider startingToggle={startingToggle}>
                <LayoutNoToggleProvider>
                </LayoutNoToggleProvider> 
            </ToggleProvider>
            <LayoutNoThemeProvider >{children}</LayoutNoThemeProvider>
        </ThemeProvider>        
        </>
        
    );
}

function LayoutNoToggleProvider ({children}) {
    const  toggle = useContext(ToggleContext);

    return (
        <div className={            
        toggle === false ? "navbar navbar-collapsed" : "navbar navbar-collapse show"
        }> 
        {children}     
        </div>
    )
}

function LayoutNoThemeProvider ({ children }) {
    const {theme} = useContext(ThemeContext);

    return (
        
        <div className={
            theme === "light" ? 
            "container-fluid bg-white" :
            "container-fluid bg-dark"  
        }>
        {children}
        </div>
    
    );
}
export default Layout;

主题文本

import React, { createContext} from "react";
import useTheme from "../hooks/useTheme";

export const ThemeContext = createContext(); 

function ThemeProvider ({children, startingTheme}) {
    const { theme, setTheme } = useTheme(startingTheme);

    return (
        <ThemeContext.Provider value={
            {theme, setTheme}
        }>    
        {children}    
        </ThemeContext.Provider>
    );

}

export { ThemeProvider };

useTheme.js

import { useState } from "react";

function useTheme (startingTheme ="light") {

    const [theme, setTheme] = useState(startingTheme);

    function validateTheme (themeValue) {
        if (themeValue === "dark") {
            setTheme("dark");
        } else {
            setTheme("light");
        }
    }    

    return {
        theme,
        setTheme: validateTheme,
    }
}

export default useTheme;

ToggleContext.js

import React, { createContext } from "react";
import useToggle from "../hooks/useToggle";

export const ToggleContext = createContext();

function ToggleProvider({ children, startingToggle }) {
  const { toggle, setToggle } = useToggle(startingToggle);

  return (
    <ToggleContext.Provider value={{ toggle, setToggle }}>
      {children}
    </ToggleContext.Provider>
  );
}

export { ToggleProvider };

usetokle.js

import { useState } from "react";

function useToggle (startingToggle = false) {
    const [toggle, setToggle] = useState(startingToggle);

    function validateShowSidebar (showSidebarValue) {
        if (showSidebarValue === "show")  {
            setToggle("show");
        } else {
            setToggle("");
        }
    }
    return {
        toggle,
        setToggle: validateShowSidebar,
    }
}

export default useToggle;

Navbar.js

import Image from "next/image";
import styles from "../../styles/Home.module.scss"
import Logo  from "../../public/Knowledge Memo.svg"
import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
import { ToggleContext } from "../contexts/ToggleContext";
import Link from 'next/link';
import { useState } from "react";


const navbarData  = [
    {   id: "1",
        title: "home",
        ref: "#home"
    },
    {   id:"2",
        title: "Skills",
        ref: "#skills"
    },
    {   id:"3",
        title: "The List",
        ref: "#theList"
    },
    {   id: "4",
        title: "Team",
        ref: "#team"
    },
    {   id: "5",
        title: "Contact",
        ref: "#contact"
    },
];

function Navbar() {

    const theme = useContext(ThemeContext);
    const toggle  = useContext(ToggleContext);   


    return (
        <>  
            
            <nav className={
                theme === "light" ? 
                "navbar navbar-expand-lg navbar-dark fixed-top": 
                "navbar navbar-expand-lg navbar-dark bg-dark fixed-top id= mainNav"}>
                <div className="container d-flex flex justify-content-between">
                    <a className="navbar-brand h-50" href="#page-top">
                    <div className="navbar-brand"> 
                    <Image 
                    src={Logo} 
                    alt="..."                  
                    fill="#fff"
                    objectFit="contain"
                    className="h-50"                    
                    />
                    </div>
                    </a>                    
                    <button
                    onClick={ () => toggle === !toggle, console.log("clicked")}
                    className="navbar-toggler collapsed" 
                    type="button" 
                    data-bs-toggle="collapsed" 
                    data-bs-target="#navbarResponsive" 
                    aria-controls="navbarResponsive" 
                    aria-expanded="false" 
                    aria-label="Toggle navigation"
                    >
                        Menu
                    <i className="fa fa-bars ms-1 navbar-toggler" aria-hidden="true"></i>
                    </button>
                    {toggle ?
                    <div className="collapsed navbar-collapse mt-2 id=navbarResponsive">
                        <ul className="navbar-nav text-uppercase ms-auto py-4 py-lg-0">
                            {navbarData.map((link,idx) => {

                                return (
                                    <li key={link.id}>
                                        <Link  href={`/${link.ref}`} className="nav-item" data-index={idx} passHref>
                                        <a className="nav-link">
                                        {link.title}
                                        </a>
                                        </Link>
                                    </li>

                                );
                            })}
                        </ul>
                    </div>
:                     <div className="collapse navbar-collapse show mt-2 id=navbarResponsive">
<ul className="navbar-nav show text-uppercase ms-auto py-4 py-lg-0">
    {navbarData.map((link,idx) => {

        return (
            <li key={link.id}>
                <Link  href={`/${link.ref}`} className="nav-item" data-index={idx} passHref>
                <a className="nav-link">
                {link.title}
                </a>
                </Link>
            </li>

        );
    })}
</ul>
</div>}
                </div>
            </nav>
        </>
    );
}

export default Navbar;

共有1个答案

齐承运
2023-03-14

您可以使用reducer来尝试此实现,以便使用localstorage处理状态更改。这不是一个确切的暗示,但你可以看到流程

在AppContext.jsx中

AppContext保存应用程序的全局状态,因此可以更轻松地使用单个上下文提供程序,并将Acton分派到特定的简化程序以处理状态更改,而无需提供多个提供程序。combinedReducer将reducer方法处理为给定的状态组件

import { useReducer, createContext, useEffect } from "react";
import userReducer from "./reducers/userReducer";
import themeReducer from "./reducers/themeReducer";
export const APP_NAME = "test_app";

//Check the localstorage or set a default state
const initialState = JSON.parse(localStorage.getItem(APP_NAME))
  ? JSON.parse(localStorage.getItem(APP_NAME))
  : {
      user: {
        username: "",
        email: "",
        isAdmin: false,
      },
      theme: { dark: false },
    };
//Create your global context
const AppContext = createContext(initialState);

//Create combined reducers
const combinedReducers = ({ user, theme }, action) => ({
  user: userReducer(user, action),
  theme: themeReducer(theme, action),
});
const AppState = ({ children }) => {
  //Making it to provider state
  const [state, dispatch] = useReducer(combinedReducers, initialState);
  useEffect(() => {
    localStorage.setItem(APP_NAME, JSON.stringify(state));
  }, [state]);
  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
};

export default AppState;

export { AppContext, AppState };

上述实现的工作原理类似于redux,但您可以将给定的状态分解为特定的缩减器来处理状态更改

在这一点上,我使用localstore来保持持久状态,因为在页面上使用上下文API重新加载状态。使用use效应钩子从反应,并在依赖数组中添加状态,以确保您的状态是同步的

UserReducer.jsx中

const userReducer = (state, action) => {
  const { type, payload } = action;
  switch (type) {
    case "LOGIN":
      return { ...state, ...payload };
    case "LOGOUT":
      return {};
    default:
      return state;
  }
};

export default userReducer;

ThemeReducer.jsx中

const themeReducer = (state, action) => {
  const { type, payload } = action;
  switch (type) {
    case "DARK":
      return { ...payload };
    default:
      return state;
  }
};

export default themeReducer;

index.jsx中使用单个提供者包装整个应用程序

import reactDom from "react-dom"
import React from "react"
import App from "./App"
import "./index.css"
import AppState from "./state/AppState"

reactDom.render(
    <React.StrictMode>
        <AppState >
            <App />
        </AppState>
    </React.StrictMode>,
    document.getElementById("root")
)

App.jsx

import { useContext } from "react";
import { AppContext } from "./state/AppState";
const App = () => {
  const { state, dispatch } = useContext(AppContext);
  const handleLogin = () => {
    dispatch({
      type: "LOGIN",
      payload: {
        username: "Mike",
        email: "mike@gmail.com",
        isAdmin: false,
      },
    });
  };

  const handleLogout = () => {
    dispatch({
      type: "LOGOUT",
      payload: {},
    });
  };

  return (
    <div className="main-container">
      <div className="container">
        <p>Username: {state.user.username ? state.user.username : "Unknown"}</p>
        <p>Email: {state.user.email ? state.user.email : "Unknown"}</p>
      </div>
      <button onClick={handleLogin}>Login</button>
      <button onClick={handleLogout} style={{ background: "red" }}>
        Login
      </button>
    </div>
  );
};

export default App;

如果你想看Github的结构,这是我的代码链接

 类似资料:
  • 首先,我想说我从来没有使用过Next.js或context api,所以请耐心听我说。 我目前正在Next.js中开发一个web应用程序,其中有多个页面,每个页面都包含一个表单。我希望有某种全局状态,以便能够设置和更新每个表单的数据。所有表单数据一起 例如:第1页=名称,第2页=说明,... 从我在网上读到的内容来看,我认为使用上下文api就足够了,但我遇到了困难。当我在第一个表单上填写名字时,它

  • 我正在尝试将openapi/swagger文件导入api网关,但无法按预期获得安全设置。我希望所有路径都需要一个api密钥。 导入后在控制台中设置它所需的api密钥是可行的,但这种解决方案是不可取的,同样可行的是在每个路径中单独设置安全字段,但我正在寻找全局解决方案。 当我尝试导入文件时,我会收到以下警告: 看起来,我要么需要一个lambda作为api密钥的自定义授权器(我不熟悉授权器,但如果我在

  • 问题内容: 我正在尝试在我的应用程序上使用React 16.3中的新上下文API进行一些测试,但我不明白为什么我的重定向永远无法工作。 我不想让我的ContextB可用于所有路由,仅2和3。我该怎么做? 问题答案: 看起来应该只具有和`<Redirect `组件作为直接子代。(来源) 我想这就是为什么你的,你使用不工作的孩子。 最简单但重复的解决方案可能是让您成为每个想要的孩子的孩子: 注意:这些

  • 我试图在我的应用程序上用React 16.3的新上下文API做一些测试,但是我不明白为什么我的重定向从来没有工作过。 我不想让我的ContextB适用于所有路线,只适用于2号和3号路线。我该怎么做?

  • 问题内容: 看起来像这样自然是很自然的: 但是Python并未为套接字实现上下文管理器。我可以轻松地将其用作上下文管理器吗? 问题答案: 该模块相当底层,使您几乎可以直接访问C库功能。 您始终可以使用装饰器来构建自己的装饰器: 或用于达到相同效果: 但是装饰器使您有机会首先使用套接字执行其他操作。 Python 3.x确实可以创建上下文管理器,但是直到2016年Python 3.5周期才进行 更新

  • 问题内容: 先前曾问过一个有关如何覆盖环境指令中定义的变量的问题,看来这是不可能的。 我想在一个阶段中设置一个变量,并使其可用于其他阶段。在声明性管道中,似乎唯一的方法是在script {}块中。 例如,我需要在结帐后设置一些变量。因此,在结帐阶段结束时,我有一个script {}块来设置这些var,并且可以在其他阶段访问它们。 这可行,但感觉不对。并且出于可读性考虑,我更愿意在管道的顶部声明这些