React基础 第十八章(在组件间共享状态)

在React应用开发中,组件间状态共享是一个常见的需求。本文将根据React官方文档的指导,详细介绍如何在组件间共享状态,包括状态提升的步骤、受控与非受控组件的概念,以及相关的开发技巧和注意事项。

状态提升

状态提升是一种常用的模式,用于在多个子组件之间共享状态。这通常是通过将状态从子组件移动到它们共同的父组件来实现的,然后通过props将状态和状态更新函数传递给子组件。

技巧

  1. 识别共享状态的组件:首先,你需要确定哪些子组件需要访问或修改相同的状态。例如,如果你有两个组件需要根据同一个条件来显示或隐藏内容,那么这个条件应该是共享的状态。
  2. 将状态移至公共父组件:接下来,你需要将这个共享状态移动到这些子组件的最近公共父组件中。这样,父组件就可以通过props将状态传递给所有需要它的子组件。
  3. 使用回调函数更新状态:为了让子组件能够更新父组件中的状态,父组件可以提供一个回调函数作为prop传递给子组件。子组件在需要更新状态时调用这个回调函数。

示例

jsx 复制代码
// 子组件
function Panel({ title, children, isActive, onShow }) {
  return (
    <section className="panel">
      <h3>{title}</h3>
      {isActive ? (
        <p>{children}</p>
      ) : (
        <button onClick={onShow}>显示</button>
      )}
    </section>
  );
}

// 父组件
function Accordion() {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <>
      <Panel
        title="关于"
        isActive={activeIndex === 0}
        onShow={() => setActiveIndex(0)}
      >
        {/* Panel内容 */}
      </Panel>
      <Panel
        title="词源"
        isActive={activeIndex === 1}
        onShow={() => setActiveIndex(1)}
      >
        {/* Panel内容 */}
      </Panel>
    </>
  );
}

在示例中,我们有两个Panel组件,它们都需要根据activeIndex的值来决定自己是否应该显示内容。这里的activeIndex是共享状态,因为它决定了哪个Panel是激活的。

Accordion父组件中,我们定义了activeIndex状态和setActiveIndex更新函数。这个状态决定了哪个Panel是活跃的。我们通过isActive prop传递给每个Panel组件一个布尔值,告诉它是否应该显示内容。我们还通过onShow prop传递了一个函数,当点击Panel的按钮时,这个函数会被调用来更新activeIndex的值。

注意事项

  • 不要在多个组件中复制状态。

正确代码

jsx 复制代码
<Panel isActive={activeIndex === 0} onShow={() => setActiveIndex(0)} />

正确的做法是在父组件中维护单一的activeIndex状态,并通过props将其传递给每个Panel组件。这样,所有的Panel组件都可以根据同一个状态来同步它们的显示状态。

错误代码

jsx 复制代码
// 错误:每个Panel都有自己的isActive状态,无法同步
<Panel isActive={isActivePanel1} onShow={() => setIsActivePanel1(true)} />
<Panel isActive={isActivePanel2} onShow={() => setIsActivePanel2(true)} />

错误的做法是在每个Panel组件中各自维护一个isActive状态。这会导致状态不同步,因为每个Panel只能控制自己的显示状态,而不是根据共享的activeIndex状态。

受控组件与非受控组件

受控组件是React中的一种模式,其中组件的状态完全由父组件控制。在受控组件中,所有的状态变化都通过父组件的props来传递,这意味着组件的状态是"受控"的,因为它不会自己维护状态,而是依赖于父组件提供的状态和回调函数。

技巧

  1. 使用受控组件来保持状态一致性:当你希望一个组件的状态能够被外部控制或者需要与其他组件同步时,应该使用受控组件。

  2. 通过props传递状态和更新函数 :你需要通过props向受控组件传递状态(如value)和一个更新状态的回调函数(如onChange)。

示例

jsx 复制代码
function ControlledInput({ value, onChange }) {
  return <input type="text" value={value} onChange={onChange} />;
}

function ParentComponent() {
  const [inputValue, setInputValue] = useState('');
  return (
    <ControlledInput
      value={inputValue}
      onChange={e => setInputValue(e.target.value)}
    />
  );
}

在提供的示例中,我们有一个ControlledInput组件,它接收两个props:valueonChange

ControlledInput组件是一个受控组件,因为它不自己维护输入框的值,而是通过value prop接收父组件传递的值,并且当输入框的值发生变化时,它通过调用onChange prop来通知父组件。

ParentComponent组件中,我们定义了一个状态inputValue和一个更新这个状态的函数setInputValue

父组件通过value prop将inputValue的值传递给ControlledInput,并且提供了一个onChange回调函数来更新这个值。当用户在输入框中输入文字时,onChange事件被触发,调用setInputValue函数,从而更新inputValue的状态。

注意事项

  • 确保受控组件的状态变化逻辑在父组件中正确处理。

正确代码

jsx 复制代码
<ControlledInput value={inputValue} onChange={e => setInputValue(e.target.value)} />

正确的做法是确保受控组件的状态变化逻辑在父组件中正确处理。这意味着你需要提供一个更新状态的回调函数,并且在受控组件中调用它。

错误代码

jsx 复制代码
// 错误:没有提供onChange处理函数,输入框将不可编辑
<ControlledInput value={inputValue} />

错误的做法是创建一个受控组件但不提供更新状态的回调函数。这会导致输入框不可编辑,因为没有方法来更新它的值。

唯一数据源原则

每个状态应该有一个唯一的地方存储,这样可以避免状态在多个地方更新导致的不一致问题。

技巧

  • 识别状态的最佳位置,并将其放在组件树中适当的层级。
  • 避免在多个组件中复制相同的状态。

示例

jsx 复制代码
// 状态存储在App组件中
function App() {
  const [userData, setUserData] = useState(null);

  // 获取用户数据的逻辑
  useEffect(() => {
    fetchUserData().then(data => setUserData(data));
  }, []);

  return (
    <UserProfile userData={userData} />
  );
}

function UserProfile({ userData }) {
  // 使用传入的userData渲染用户信息
}

注意事项

  • 避免在不同的组件中同步更新相同的状态。

通过遵循这些原则和技巧,你可以在React应用中有效地在组件间共享状态,同时保持代码的清晰和可维护性。记住,状态的组织和管理是构建可靠React应用的关键。

相关推荐
吕彬-前端12 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白33 分钟前
react hooks--useCallback
前端·react.js·前端框架
恩婧41 分钟前
React项目中使用发布订阅模式
前端·react.js·前端框架·发布订阅模式
mez_Blog42 分钟前
个人小结(2.0)
前端·javascript·vue.js·学习·typescript
珊珊而川1 小时前
【浏览器面试真题】sessionStorage和localStorage
前端·javascript·面试
森叶1 小时前
Electron 安装包 asar 解压定位问题实战
前端·javascript·electron
drebander1 小时前
ubuntu 安装 chrome 及 版本匹配的 chromedriver
前端·chrome
软件技术NINI1 小时前
html知识点框架
前端·html
深情废杨杨1 小时前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS1 小时前
【vue3】vue3.3新特性真香
前端·javascript·vue.js