我有一个NoteItem组件,它从内部的数组递归地呈现自己,但在最顶层的组件上,当我在顶级父级NoteItem使用redux函数时,我可以使用操作和访问状态,但当我尝试使用它时,它会在嵌套的NoteItem组件内,我认为我们可以在任何地方使用redux,并访问状态和功能以及
这是父边栏组件
边栏.js
import React, { Component } from "react";
import { Popover, Tooltip } from "antd";
import { connect } from "react-redux";
import "./sidebar.css";
import settingsIcon from "../icons/settings.svg";
import addIcon from "../icons/plus.svg";
import NoteItem from "../NoteItem/NoteItem";
import { getNotes, createNote } from "../actions/noteActions";
import store from "../store";
import { SET_CONTEXTMENU_VISIBLE, SET_ACTIVE_NOTE } from "../actions/types";
class Sidebar extends Component {
state = {
notes: this.props.notes,
ownUpdate: false,
xPos: 0,
yPos: 0,
mouseDownOnMenu: this.props.mouseDownOnMenu,
contextMenuVisible: this.props.contextMenuVisible,
};
static getDerivedStateFromProps(props, state) {
if (state.ownUpdate) {
return {
...state,
ownUpdate: false,
};
} else {
if (props.notes != state.notes) {
return {
notes: props.notes,
};
}
if (props.contextMenuVisible != state.contextMenuVisible) {
return {
contextMenuVisible: props.contextMenuVisible,
};
}
if (props.mouseDownOnMenu != state.mouseDownOnMenu) {
return {
mouseDownOnMenu: props.mouseDownOnMenu,
};
}
}
return null;
}
addNote = () => {
const newNote = {
name: "Untitled",
};
this.props.createNote(newNote);
};
componentDidMount() {
window.addEventListener("mousedown", (e) => {
const { mouseDownOnMenu } = this.state;
if (mouseDownOnMenu) {
return;
}
store.dispatch({
type: SET_CONTEXTMENU_VISIBLE,
payload: false,
});
store.dispatch({
type: SET_ACTIVE_NOTE,
payload: undefined,
});
});
this.props.getNotes();
}
componentWillUnmount() {
window.removeEventListener("mousedown", window);
}
addSubNote = (note, path) => {
this.props.addSubNote(note, path);
};
setMouseDownActive = () => {
this.setState({
mouseDownOnMenu: !this.state.mouseDownOnMenu,
});
};
render() {
const { notes, contextMenuVisible, xPos, yPos } = this.state;
return (
<div className="sidebar-container">
<div className="header-container">
<h4>Username</h4>
<Tooltip title="Settings" placement="right">
<img src={settingsIcon} />
</Tooltip>
</div>
<div className="sidebar-content-container">
<div className="sidebar-controls-container">
<h4>Notes</h4>
<div className="add-icon-button" onClick={this.addNote}>
<img src={addIcon} />
</div>
</div>
<div className="sidebar-page-container">
{notes &&
notes.map((note, index) => (
<NoteItem note={note} key={note._id}></NoteItem> // I can use all the functions and
// access the state in these components
))}
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
notes: state.note.notes,
contextMenuVisible: state.note.contextMenuVisible,
mouseDownOnMenu: state.note.mouseDownOnMenu,
};
};
const mapDispatchToProps = (dispatch) => {
return {
createNote: (noteData) => dispatch(createNote(noteData)),
getNotes: () => dispatch(getNotes()),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Sidebar);
这是我的NoteItem组件
NoteItem.js
import React, { Component } from "react";
import "./noteitem.css";
import pageIcon from "../icons/file-text.svg";
import { connect } from "react-redux";
import addIcon from "../icons/plus.svg";
import caretIcon from "../icons/play.svg";
import { Popover, Button, Input, Menu } from "antd";
import { addSubNote, deleteNote } from "../actions/noteActions";
import { DeleteOutlined, EditOutlined, LinkOutlined } from "@ant-design/icons";
import {
SET_ACTIVE_NOTE,
SET_CONTEXTMENU_VISIBLE,
SET_MOUSEDOWNON_MENU,
} from "../actions/types";
import axios from "axios";
import apiUrl from "../utils/getApiUrl";
import store from "../store";
class NoteItem extends Component {
state = {
subNoteOpen: false,
contextMenuVisible: this.props.contextMenuVisible,
xPos: 0,
yPos: 0,
deleteNoteActive: false,
renamePopupVisible: false,
note: this.props.note,
ownUpdate: false,
activeNote: this.props.activeNote,
subNotes: [],
mouseDownOnMenu: this.props.mouseDownOnMenu,
};
componentDidUpdate(prevProps, prevState, snap) {
if (
prevProps.activeNote != this.props.activeNote &&
this.props.activeNote
) {
this.setState({
activeNote: this.props.activeNote,
ownUpdate: true,
});
}
}
static getDerivedStateFromProps(props, state) {
if (state.ownUpdate) {
return {
...state,
ownUpdate: false,
};
} else {
if (props.note != state.note) {
return {
note: props.note,
};
}
if (props.contextMenuVisible != state.contextMenuVisible) {
return {
contextMenuVisible: props.contextMenuVisible,
};
}
if (props.mouseDownOnMenu != state.mouseDownOnMenu) {
return {
mouseDownOnMenu: props.mouseDownOnMenu,
};
}
if (props.activeNote != state.activeNote) {
return {
activeNote: props.activeNote,
};
}
}
return null;
}
getSubNotes = async () => {
try {
const { note } = this.state;
if (note) {
const subNotes = await axios.get(`${apiUrl}/note/sub-notes`, {
headers: {
"Content-Type": "application/json",
},
params: {
id: note._id,
},
});
this.setState({
subNotes: subNotes.data,
});
}
} catch (err) {
console.log(err);
}
};
addNewSubNote = async (note) => {
try {
const subNote = await axios.post(
`${apiUrl}/note/subnote/new`,
{
note: {
name: "Untitled",
},
path: note.path,
},
{
headers: {
"Content-Type": "application/json",
},
}
);
this.setState({
subNotes: [...this.state.subNotes, subNote.data],
subNoteOpen: true,
});
} catch (err) {
console.log(err);
}
};
getRenameNoteInput = () => {
return (
<div className="rename-note-container">
<Input placeholder="Enter name" />
<Button type="primary">Save</Button>
</div>
);
};
toggleSubNote = () => {
this.setState(
{
subNoteOpen: !this.state.subNoteOpen,
},
() => {
if (this.state.subNoteOpen) {
this.getSubNotes();
}
}
);
};
toggleContextMenu = (e, note) => {
e.preventDefault();
var body = document.body,
html = document.documentElement;
var height = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight
);
if (e.pageY > height - 100) {
this.setState(
{
xPos: e.pageX,
yPos: e.pageY - 130,
},
() => {
store.dispatch({
type: SET_ACTIVE_NOTE,
payload: note,
});
}
);
} else {
this.setState(
{
xPos: e.pageX,
yPos: e.pageY,
},
() => {
store.dispatch({
type: SET_ACTIVE_NOTE,
payload: note,
});
}
);
}
store.dispatch({
type: SET_CONTEXTMENU_VISIBLE,
payload: true,
});
};
deleteNote = ({ domEvent }) => {
domEvent.preventDefault();
store.dispatch({
type: SET_MOUSEDOWNON_MENU,
payload: true,
});
store.dispatch({
type: SET_CONTEXTMENU_VISIBLE,
payload: false,
});
const { note } = this.state;
console.log(this.props);
this.props.deleteNote(note);
};
setMouseDownActive = () => {
store.dispatch({
type: SET_MOUSEDOWNON_MENU,
payload: !this.state.mouseDownOnMenu,
});
};
render() {
const {
subNoteOpen,
note,
renamePopupVisible,
activeNote,
subNotes,
contextMenuVisible,
xPos,
yPos,
} = this.state;
return (
<div style={{ width: "100%" }}>
{activeNote && contextMenuVisible && activeNote._id == note._id && (
<div
className="context-menu"
style={{ position: "fixed", top: yPos, left: xPos }}
>
<Menu
style={{ width: "200px", borderRadius: "3px" }}
onMouseDown={this.setMouseDownActive}
onMouseUp={this.setMouseDownActive}
>
<Menu.Item icon={<EditOutlined />}>Rename</Menu.Item>
<Menu.Item icon={<LinkOutlined />}>Copy Link</Menu.Item>
<Menu.Item icon={<DeleteOutlined />} onClick={this.deleteNote}>
Delete
</Menu.Item>
</Menu>
</div>
)}
<Popover
content={this.getRenameNoteInput()}
placement="bottom"
overlayClassName="no-arrow"
visible={renamePopupVisible}
>
<div
className={
activeNote && activeNote._id == note._id
? "page-item page-item-active"
: "page-item"
}
onContextMenu={(e) => this.toggleContextMenu(e, note)}
>
<img
src={caretIcon}
onClick={this.toggleSubNote}
className={subNoteOpen ? "caret-open" : "caret"}
/>
<img src={pageIcon} />
<span>{note.name}</span>
<div
className="add-page-button"
onClick={() => this.addNewSubNote(note)}
>
<img src={addIcon} />
</div>
</div>
</Popover>
<div
className={
subNoteOpen
? "sub-page-container subpage-open"
: "sub-page-container"
}
>
{subNotes.length > 0 ? (
<div>
{subNotes.map((subNote) => (
<NoteItem note={subNote} key={subNote._id} /> // I can't use any redux functions or
// access redux state in these components
))}
</div>
) : (
<p className="empty-text">Empty</p>
)}
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
activeNote: state.note.activeNote,
contextMenuVisible: state.note.contextMenuVisible,
mouseDownOnMenu: state.note.mouseDownOnMenu,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addSubNote: (note, path) => dispatch(addSubNote(note, path)),
deleteNote: (note) => dispatch(deleteNote(note)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(NoteItem);
连接的默认导出的NoteItem组件接收到道具,但随后它会递归地呈现未连接的版本的NoteItem。那个版本对redux商店一无所知。
你可以做一些类似的事情
const ConnectedNoteItem = connect(mapStateToProps, mapDispatchToProps)(
class NoteItem extends Component {
// ... use ConnectedNoteItem in here
})
export default ConnectedNoteItem