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;
}
}