Skip to content
鼓励作者:欢迎打赏犒劳

react组件通信

非父子通讯-兄弟组件

实现也很简单,就是找到共同的父级来实现数据的传递

jsx
import classNames from 'classnames'
import { useState } from 'react'
import './App.scss'

// 兄弟组件通讯

// 左侧 好友列表组件
const Friends = ({ friends, onSelect, chatFriend }) => {
  return (
    <div className="friends">
      {friends.map(item => {
        return (
          <div
            key={item.id}
            className={classNames(
              'friend',
              item.id === chatFriend.id && 'selected'
            )}
            onClick={() => onSelect(item)}
          >
            <img src={item.avatar} className="avatar" alt="" />
            <div className="info">
              <div className="row">
                <div className="name">{item.name}</div>
                <div className="date">{item.dateStr}</div>
              </div>
              <div className="msg">{item.message}</div>
            </div>
          </div>
        )
      })}
    </div>
  )
}

// 右侧 聊天窗口组件
const Chat = ({ friend }) => {
  return (
    <div className="chat-wrapper">
      <div className="header">{friend.name}</div>
      <div className="list"></div>
      <div className="input"></div>
    </div>
  )
}

// 好友列表数据
const defaultFriends = [
  {
    id: '13258165',
    name: '周杰伦',
    avatar:
      'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/reactbase/comment/zhoujielun.jpeg',
    dateStr: '刚刚',
    message: '哎呦,不错哦',
  },
  {
    id: '36080105',
    name: '许嵩',
    avatar:
      'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/reactbase/comment/xusong.jpeg',
    dateStr: '01/05',
    message: '[语音]',
  },
]
// 父组件
const App = () => {
  const [friends, setFriends] = useState(defaultFriends)

  // 1. 找到父组件,提供要共享的数据
  const [chatFriend, setChatFriend] = useState(friends[0])

  const onSelectFriend = friend => {
    setChatFriend(friend)
  }

  return (
    <div className="app">
      {/* 好友列表 */}
      {/* 3. 通过子到父通讯,来修改选中的好友 */}
      <Friends
        friends={friends}
        onSelect={onSelectFriend}
        chatFriend={chatFriend}
      />
      {/* 聊天窗口 */}
      {/* 2. 通过父到子通讯,来展示好友名称 */}
      <Chat friend={chatFriend} />
    </div>
  )
}

export default App
scss
.app {
  display: flex;
  height: 500px;
  width: 600px;
  margin: 50px auto 0;
  border: 1px solid #f3f3f3;
}

// 左侧好友列表样式
.friends {
  width: 240px;
  height: 100%;
  border-right: 1px solid #e0e0e0;

  .selected {
    background-color: #dedede;
  }
  .friend {
    display: flex;
    width: 100%;
    padding: 10px;
    box-sizing: border-box;
    cursor: pointer;

    .avatar {
      width: 40px;
      height: 40px;
      margin-right: 10px;
      border-radius: 5px;
    }

    .info {
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      flex: 1;

      .row {
        display: flex;
        justify-content: space-between;

        .name {
          font-size: 16px;
          line-height: 1;
          color: #333;
        }
        .date {
          color: #9a9a9a;
          font-size: 12px;
        }
      }

      .msg {
        color: #9a9a9a;
        font-size: 14px;
      }
    }
  }
}

// 右侧聊天框样式
.chat-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #f3f3f3;
  .header {
    height: 60px;
    padding-left: 25px;
    line-height: 60px;
    border-bottom: 1px solid #e0e0e0;
    font-size: 18px;
  }
  .list {
    flex: 1;
  }
  .input {
    height: 130px;
    border-top: 1px solid #e0e0e0;
  }
}

非父子通讯-跨组件通讯-Context

需求是:点击父组件的颜色选择器,会影响孙子组件【菜单】的文本颜色,点击孙子组件【重置主题按钮】会重置初始颜色

下面的例子是一个比较复杂的父子孙组件嵌套的例子,利用全局Context来实现数据的通信。

1、createContext() 先创建一个Context对象

2、<ThemeContext.Provider value="要共享的变量"></ThemeContext.Provider> 用这个包裹范围

3、useContext(ThemeContext) 取出共享值

jsx
import { useContext, useState } from "react";
import { createContext } from "react";
import "./App.scss";

// 跨组件通讯 - Context
// 1. 创建 Context 对象
const ThemeContext = createContext();

// 2. 划定范围,提供共享数据
// 3. 范围内的组件,获取共享数据

// -------------------------侧边栏-----------------------
const Sidebar = () => {
  return (
    <div className="sidebar">
      <Menu />
    </div>
  );
};
const Menu = () => {
  return (
    <div className="menu">
      <ul>
        <MenuItem />
        <MenuItem />
      </ul>
    </div>
  );
};
const MenuItem = () => {
  // 3. 范围内的组件,获取共享数据
  const { theme } = useContext(ThemeContext);
  return <li style={{ color: theme }}>菜单</li>;
};

// -------------------------右侧内容-----------------------
const Content = () => {
  const { theme } = useContext(ThemeContext);
  return (
    <div className="content">
      <div className="main" style={{ color: theme }}>
        Context 跨组件通讯
      </div>
      <Footer />
    </div>
  );
};
const Footer = () => {
  const { onReset } = useContext(ThemeContext);

  return (
    <div className="footer">
      <button onClick={onReset}>重置主题</button>
    </div>
  );
};

// 父组件
const App = () => {
  // 要共享的主题颜色
  const [theme, setTheme] = useState("#1677FF");

  // 重置主题的函数
  const onReset = () => {
    setTheme("#1677FF");
  };

  return (
    <div className="app">
      {/* 2. 划定范围,提供共享数据 */}
      <ThemeContext.Provider
        value={{
          theme,
          onReset,
          // ...
        }}
      >
        {/* 默认颜色: #1677FF */}
        <input
          className="theme-selector"
          type="color"
          value={theme}
          onChange={(e) => setTheme(e.target.value)}
        />

        <div className="main">
          {/* 侧边栏 */}
          <Sidebar />
          {/* 右侧内容 */}
          <Content />
        </div>
      </ThemeContext.Provider>
    </div>
  );
};

export default App;
scss
.app {
  position: relative;
  height: 500px;
  width: 600px;
  margin: 50px auto 0;
  border: 1px solid #d5d5d5;
}

.theme-selector {
  position: absolute;
  right: 0;
  top: 0;
}

.main {
  display: flex;
  height: 100%;
}

.sidebar {
  width: 150px;
  height: 100%;
  border-right: 1px solid #d5d5d5;
}

.menu {
  li {
    margin: 10px 0;
  }
}

.header {
  padding: 10px 0;
  text-align: center;
}

.content {
  flex: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 10px;
  box-sizing: border-box;

  .main {
    flex: 1;
  }

  .footer {
    height: 100px;
    padding-top: 20px;
    border-top: 1px solid #d5d5d5;
    box-sizing: border-box;
    text-align: center;
  }
}

如有转载或 CV 的请标注本站原文地址