我目前正在尝试将后端数据库连接到redux存储,并将redux存储连接到前端react应用程序。数据库和redux都很好,运行良好。我需要知道的是如何放置我的";getItems(("函数,这样我的redux商店就可以在不破坏我的react应用程序的情况下从数据库中获取项目。我目前的方法使用componentDidMount((来为getItems((函数计时,但这会导致问题,在此之前我希望做进一步的工作。我曾考虑过使用constructor((生命周期方法,或者使用mapStateToProps((函数中的一些方法,但这两种方法都被奇怪地弃用/或文档不好。我想知道是否有人能给我一些具体的答案或类似的真实例子。
MusicList.js:
import React, {Component, useState } from "react";
import Items from "./Items";
import AddItem from "./AddItem";
import { connect } from "react-redux";
import { getItems, deleteItem, addItem, toggleItem } from "../actions/itemActions"
import PropTypes from "prop-types";
import AppNavbar from "./AppNavbar.js";
class MusicList extends Component {
componentDidMount = () => {
this.props.getItems();
}
toggle = (id) => {
this.props.toggleItem(id)
console.log(this.props.item.items)
}
delItem = (id) => {
this.props.deleteItem(id)
}
addItem = (item, url) => {
this.props.addItem(item, url)
console.log(this.props.item.items)
}
render() {
const { items } = this.props.item
console.log(items)
return (
<div>
<div class="sticky-top">
{/* <AppNavbar toggle={this.toggle} items={items}/> */}
<AddItem addItem={this.addItem}/>
</div>
<Items items={items} toggle={this.toggle} delItem={this.delItem} />
</div>
)
}
}
MusicList.propTypes = {
getItems: PropTypes.func.isRequired,
deleteItem: PropTypes.func.isRequired,
addItem: PropTypes.func.isRequired,
toggleItem: PropTypes.func.isRequired,
item: PropTypes.object.isRequired
}
const mapStateToProps = (state) => (
{
item: state.item
}
)
export default connect(mapStateToProps, { getItems, deleteItem, addItem, toggleItem})(MusicList);
Redux文件:
index.js:
import { combineReducers } from "redux";
import itemReducer from "./itemReducer";
export default combineReducers({
item: itemReducer
})
types.js:
export const GET_ITEMS = "GET_ITEMS";
export const ADD_ITEM = "ADD_ITEM";
export const DELETE_ITEM = "DELETE_ITEM";
export const TOGGLE_ITEM = "TOGGLE_ITEM";
export const ITEMS_LOADING = "ITEMS_LOADING";
itemActions.js:
import axios from "axios"
import { GET_ITEMS, ADD_ITEM, DELETE_ITEM, TOGGLE_ITEM, ITEMS_LOADING } from "./types"
export const getItems = () => dispatch => {
dispatch(setItemsLoading());
axios
.get("/music_list/items")
.then(res => dispatch({
type: GET_ITEMS,
payload: res.data
}))
}
export const deleteItem = (id) => dispatch => {
axios.delete(`/music_list/items/${id}`)
.then(res => dispatch ({
type: DELETE_ITEM,
payload: id
}))
}
export const addItem = (item, url) => dispatch => {
console.log(item, url)
if (url.match(/soundcloud.com/)) {
let data = {
"url": url,
"item": item
}
axios
.post("/music_list/items", data)
.then(res => dispatch({
type: ADD_ITEM,
payload: res.data
}))
}
else if (url.match(/youtube.com/)) {
let data = {
"url": url,
"item": item
}
axios
.post("/music_list/items", data)
.then(res => dispatch({
type: ADD_ITEM,
payload: res.data
}))
// const regexID = /(?<=v=).*/
// let id = url.match(regexID)
// async function newItem(id) {
// const item = await titleScrape(id)
// // let html = json.html.match(/(?<=url=).{53}/).toString()
// let newItem = {
// url: item.object.url,
// name: item.object.name,
// isOpen: false
// }
// return newItem
// }
// newItem(id)
// .then(item => axios.post("/music_list/items", item).then(res => dispatch({
// type: ADD_ITEM,
// payload: res.data
// })))
}
}
export const toggleItem = (id) => {
return {
type: TOGGLE_ITEM,
payload: id
}
}
export const setItemsLoading = () => {
return {
type: ITEMS_LOADING
}
}
itemReducer.js:
import { GET_ITEMS, ADD_ITEM, DELETE_ITEM, TOGGLE_ITEM, ITEMS_LOADING } from "../actions/types"
const initialState = {
items: [],
loading: false
}
export default function(state = initialState, action) {
switch(action.type) {
case GET_ITEMS:
console.log(action.payload)
return {
...state,
items: action.payload,
loading: false
};
case DELETE_ITEM:
return {
items: state.items.filter(
item => item._id !== action.payload
)
}
case ADD_ITEM:
console.log(action.payload)
return {
items: [...state.items, action.payload]
}
case TOGGLE_ITEM:
return {
items: state.items.map(item => {
if(item._id == action.payload) {
item.isOpen = !item.isOpen
}
else{
item.isOpen = false
}
return item;
})
}
case ITEMS_LOADING:
return {
...state,
loading: true
}
default:
return state;
}
}
AppNavbar.js:
import React, { Component } from "react";
import {
Navbar,
NavbarBrand,
Nav,
Button
} from "react-bootstrap";
import './style.css';
import PropTypes from "prop-types";
class AppNavbar extends Component {
playTrack = (track_index, items, placeholderTitle, isPlaying, isShuffling) => {
console.log(items)
isPlaying = true;
// playpause_btn.innerHTML = '<i class="fa fa-pause-circle fa-5x"></i>';
// Load a new track
if (isShuffling) {
track_index = Math.floor(Math.random() * items.length)
items[track_index].this.props.toggle()
// this.props.toggle(trackIndexToID)
}
else {
items[track_index].this.props.toggle()
}
// Update details of the track
placeholderTitle = this.props.items[track_index].name;
// Move to the next track if the current finishes playing
// using the 'ended' event
// items[track_index].addEventListener("ended", nextTrack);
}
playpauseTrack = (track_index, isPlaying, items, placeholderTitle, isShuffling, playTrack, pauseTrack) => {
// Switch between playing and pausing
// depending on the current state
if (!isPlaying) playTrack(track_index, items, placeholderTitle, isPlaying, isShuffling);
else pauseTrack(track_index, items, placeholderTitle, isPlaying);
}
pauseTrack = (track_index, items, placeholderTitle, isPlaying) => {
// Pause the loaded track
isPlaying = false;
// Replace icon with the play icon
// playpause_btn.innerHTML = '<i class="fa fa-play-circle fa-5x"></i>';
items[track_index].this.props.toggle()
}
nextTrack = (track_index, isLooping, items, playTrack) => {
// Go back to the first track if the
// current one is the last in the track list
if (track_index < items.length - 1 && !isLooping) {
track_index += 1;
}
else if (isLooping) {
track_index = track_index
}
else track_index = 0;
// Load and play the new track
playTrack(track_index);
}
prevTrack = (track_index, isLooping, items, playTrack) => {
// Go back to the last track if the
// current one is the first in the track list
if (track_index > 0 && !isLooping) {
track_index -= 1;
}
else if (isLooping) {
track_index = track_index
}
else track_index = items.length;
// Load and play the new track
playTrack(track_index);
}
// Connect to loop track icon
loopTrack = (element, isLooping, isShuffling, loopOn, shuffleOff, LoopOff) => {
if (!isLooping) {
loopOn(element, isLooping);
shuffleOff(element, isShuffling);
}
else LoopOff(element, isLooping);
}
// Connect to shuffle track icon
shuffleTrack = (element, isLooping, isShuffling, shuffleOn, LoopOff, shuffleOff) => {
if (!isShuffling) {
shuffleOn(element, isShuffling);
LoopOff(element, isLooping);
}
else shuffleOff(element, isShuffling);
}
loopOn = (element, isLooping) => {
isLooping = true;
element.style.opacity = "1"
console.log("1" + isLooping)
}
LoopOff = (element, isLooping) => {
isLooping = false;
element.style.opacity = "0.8"
console.log("2" + isLooping)
}
shuffleOn = (element, isShuffling) => {
isShuffling = true;
element.style.opacity = "1"
console.log("3" + isShuffling)
}
shuffleOff = (element, isShuffling) => {
isShuffling = false;
element.style.opacity = "0.8"
console.log("4" + isShuffling)
}
render() {
console.log(this.props.items)
var placeholderTitle = "Who asked (Feat: Nobody)";
let track_index = 0;
let isShuffling = false;
let isLooping = false;
let isPlaying = false;
return (
<Navbar className="Navbar">
<NavbarBrand id="Logo" href="#home">
<img
alt=""
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Logo-Smaller.png')}
/>
</NavbarBrand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<ul className="navbar-nav">
<li>
<a href="#" className="Title">
Embedded Music
</a>
</li>
</ul>
</Nav>
<Nav className="mr-auto">
<div id="PlayerBox">
<ul className="navbar-nav">
<li>
<header id="Player">
Now Playing:
</header>
<header id="Player">
{placeholderTitle}
</header>
<img class="Previous transparent" id="Player"
alt=""
onClick={this.prevTrack(track_index, isLooping, this.props.items, this.playTrack())}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Previous-smallest.png')}
/>
<img class="playpauseTrack transparent" id="Player"
alt=""
onClick={this.playpauseTrack(track_index, isPlaying, this.props.items, placeholderTitle, isShuffling, this.playTrack(), this.pauseTrack())}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Play-smallest.png')}
/>
<img class="Next transparent" id="Player"
alt=""
onClick={this.nextTrack(track_index, isLooping, this.props.items, this.playTrack())}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Next-smallest.png')}
/>
<img class="Loop transparent" id="Player"
alt=""
onClick={this.loopTrack(this, isLooping, isShuffling, this.loopOn(), this.shuffleOff(), this.LoopOff())}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Loop-small.png')}
/>
<img class="Shuffle transparent" id="Player"
alt=""
onClick={this.shuffleTrack(this, isLooping, isShuffling, this.shuffleOn(), this.LoopOff(), this.shuffleOff())}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Shuffle-smallest.png')}
/>
</li>
</ul>
</div>
</Nav>
<ul className="navbar-nav">
<li>
<a href="#Help/FAQ">
<Button className="btn btn-secondary Button">Help/FAQ</Button>
</a>
</li>
<li>
<a href="#Contact">
<Button className="btn btn-secondary Button">Contact</Button>
</a>
</li>
<li>
<a href="#Donate">
<Button className="btn btn-secondary Button">Donate</Button>
</a>
</li>
</ul>
</Navbar.Collapse>
</Navbar>
)
}
}
AppNavbar.propTypes = {
toggle: PropTypes.func.isRequired,
items: PropTypes.array.isRequired
}
export default AppNavbar;
好吧,我的AppNavbar.js组件中有很多东西都搞砸了。我调用了JSX中的函数,而不是引用它们,因为它们会在状态结束之前运行并破坏我的程序。这是更新后的程序。我还把所有东西都搬到了州里。感谢@DrewReese的宝贵帮助。
class AppNavbar extends Component {
state = {
placeholderTitle: "Who asked (Feat: Nobody)",
track_index: 0,
isShuffling: false,
isLooping: false,
isPlaying: false
};
playTrack = () => {
console.log(this.state.track_index)
this.state.isPlaying = true;
// playpause_btn.innerHTML = '<i class="fa fa-pause-circle fa-5x"></i>';
// Load a new track
if (this.state.isShuffling) {
this.setState({
track_index: Math.floor(Math.random() * this.props.items.length)
})
this.props.toggle(this.props.items[this.state.track_index]._id)
}
else {
this.props.toggle(this.props.items[this.state.track_index]._id)
}
// Update details of the track
this.setState({
placeholderTitle: this.props.items[this.state.track_index].name
})
console.log(this.props.items[this.state.track_index])
// Move to the next track if the current finishes playing
// using the 'ended' event
}
playpauseTrack = () => {
// Switch between playing and pausing
// depending on the current state
if (!this.state.isPlaying) this.playTrack();
else this.pauseTrack();
}
pauseTrack = () => {
// Pause the loaded track
this.setState({
isPlaying: false
})
// Replace icon with the play icon
// playpause_btn.innerHTML = '<i class="fa fa-play-circle fa-5x"></i>';
this.props.toggle(this.props.items[this.state.track_index]._id)
}
nextTrack = () => {
// Go back to the first track if the
// current one is the last in the track list
// much preferred imo. very easy to reason about, probably more performant
const settingTempState = () => {
// this acts as a sort of "draft" version of the next state
const tempState = { ...this.state }
// you use it throughout the function instead of this.state
if (tempState.track_index < this.props.items.length - 1 && !tempState.isLooping) {
tempState.track_index = tempState.track_index + 1
}
else if (tempState.isLooping) {
tempState.track_index = tempState.track_index
}
else {
tempState.track_index = 0
}
console.log(tempState.track_index)
// only one state update occurs
this.setState(tempState, () => this.playTrack())
}
settingTempState()
console.log(this.state.track_index)
// Load and play the new track
}
prevTrack = () => {
// Go back to the last track if the
// current one is the first in the track list
const settingTempState = () => {
// this acts as a sort of "draft" version of the next state
const tempState = { ...this.state }
// you use it throughout the function instead of this.state
if (tempState.track_index > 0 && !tempState.isLooping) {
tempState.track_index = tempState.track_index - 1
}
else if (tempState.isLooping) {
tempState.track_index = tempState.track_index
}
else {
tempState.track_index = this.props.items.length - 1
}
// only one state update occurs
this.setState(tempState, () => this.playTrack())
}
settingTempState()
console.log(this.state.track_index)
// Load and play the new track
}
// Connect to loop track icon
loopTrack = () => {
if (!this.state.isLooping) {
this.loopOn();
this.shuffleOff();
}
else this.LoopOff();
}
// Connect to shuffle track icon
shuffleTrack = () => {
if (!this.state.isShuffling) {
this.shuffleOn();
this.LoopOff();
}
else this.shuffleOff();
}
loopOn = () => {
this.setState({
isLooping: true
})
console.log("1" + this.state.isLooping)
}
LoopOff = () => {
this.setState({
isLooping: false
})
console.log("2" + this.state.isLooping)
}
shuffleOn = () => {
this.setState({
isShuffling: true
})
console.log("3" + this.state.isShuffling)
}
shuffleOff = () => {
this.setState({
isShuffling: false
})
console.log("4" + this.state.isShuffling)
}
render() {
console.log(this.props.items)
return (
<Navbar className="Navbar">
<NavbarBrand id="Logo" href="#home">
<img
alt=""
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Logo-Smaller.png')}
/>
</NavbarBrand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<ul className="navbar-nav">
<li>
<a href="#" className="Title">
Embedded Music
</a>
</li>
</ul>
</Nav>
<Nav className="mr-auto">
<div id="PlayerBox">
<ul className="navbar-nav">
<li>
<header id="Player">
Now Playing:
</header>
<header id="Player">
{this.state.placeholderTitle}
</header>
<img class="btnPrev Previous transparent" id="Player"
alt=""
onClick={this.prevTrack}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Previous-smallest.png')}
/>
<img class="btnPlayPauseTrack playpauseTrack transparent" id="Player"
alt=""
onClick={this.playpauseTrack}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Play-smallest.png')}
/>
<img class="btnNext Next transparent" id="Player"
alt=""
onClick={this.nextTrack}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Next-smallest.png')}
/>
<img class="btnLoop Loop transparent" id="Player" style={{opacity: this.state.isLooping ? "1" : "0.8"}}
alt=""
onClick={this.loopTrack}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Loop-small.png')}
/>
<img class="btnShuffle Shuffle transparent" id="Player" style={{opacity: this.state.isShuffling ? "1" : "0.8"}}
alt=""
onClick={this.shuffleTrack}
src={require('D:/Parrot/Desktop/Dev/Duplicate/Embedded_Music_Player/client/src/images/Shuffle-smallest.png')}
/>
</li>
</ul>
</div>
</Nav>
<ul className="navbar-nav">
<li>
<a href="#Help/FAQ">
<Button className="btn btn-secondary Button">Help/FAQ</Button>
</a>
</li>
<li>
<a href="#Contact">
<Button className="btn btn-secondary Button">Contact</Button>
</a>
</li>
<li>
<a href="#Donate">
<Button className="btn btn-secondary Button">Donate</Button>
</a>
</li>
</ul>
</Navbar.Collapse>
</Navbar>
)
}
}
AppNavbar.propTypes = {
toggle: PropTypes.func.isRequired,
items: PropTypes.array.isRequired
}
export default AppNavbar;