I get an error saying
You must pass a component to the function returned by connect.
Instead received undefined.
I don't know why this is happening. It works with my Checkout.js component, but not my Store.js component. Why is it working for Checkout but not Store? I want to have access to {total} like I do in the Checkout component.
This is my container Store.js
import { connect } from 'react-redux';
import { getItems, getCurrency, getTotal} from '../ducks/cart';
import Store from '../components/Store';
const mapStateToProps = (state, props) => {
return {
total: getTotal(state, props)
}
}
export default connect(mapStateToProps)(Store);
And this is my component Store.js
import React, {Component} from 'react';
import { PropTypes } from 'react';
import Home from '../components/Home';
import Cart from '../containers/Cart';
import ProductList from '../containers/ProductList';
import Checkout from '../containers/Checkout';
class Store extends Component{
render(){
return(
<div className="container">
<div className="row">
<div className="col-md-12">
<h3>Armor and Weapon Store</h3>
<h4 className="badge badge-warning margin-right">Gold: </h4>
</div>
</div>
<div className="row">
<div className="col-md-8">
<ProductList />
</div>
<div className="col-md-4">
<Cart />
<Checkout />
</div>
</div>
</div>
);
}
}
Store.propTypes = {
total: PropTypes.number,
}
export default Store;
Here is the container Checkout.js that works
import { connect } from 'react-redux';
import { getItems, getCurrency, getTotal} from '../ducks/cart';
import Checkout from '../components/Checkout/Checkout';
const mapStateToProps = (state, props) => {
return {
total: getTotal(state, props)
}
}
export default connect(mapStateToProps)(Checkout);
And the component Checkout.js
import React, { PropTypes } from 'react';
import {Component} from 'react';
import Home from '../components/Home.js';
class Checkout extends Component{
buttonClick = () => {
let { total } = this.props;
var home = new Home
var max = home.getMax()
console.log(total);
console.log(max);
if (max >= total) {
max = max - total;
console.log(max);
}
else {
console.log('Not enough gold!');
}
}
render() {
return(
<div>
<button type="button" onClick={this.buttonClick}> Checkout </button>
</div>
);
}
}
Checkout.propTypes = {
total: PropTypes.number,
}
export default Checkout;
Related
Trying to share the state between 2 components so the dashboard and the displayed component can change states simultaneously. Exmp: Switch from dashboard view to application view and display application component. Problem: I can't wrap my head around what I'm missing here and why the state does not work on my dashboard component.
Redux Slice:
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
selector: 0,
};
export const dashboardSlice = createSlice({
name: 'mainDashState',
initialState,
reducers: {
setSelector: (state, action) => {
state.selector = action.payload;
},
},
});
export const { setSelector } = dashboardSlice.actions;
export default dashboardSlice.reducer;
Redux Store:
import { configureStore } from "#reduxjs/toolkit";
import mainDashSlice from './slices/mainDashSlice';
export const store = configureStore({
reducer: {
mainDashState: mainDashSlice,
},
})
Dashboard.jsx:
import { React } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import '../../css/SideBar.css';
import { UilSignOutAlt } from '#iconscout/react-unicons';
import Logo from '../../../../assets/img/companyLogo.jpg';
import { SidebarData } from '../../Data/Data';
import { setSelector } from '../../../../features/redux/slices/mainDashSlice';
// import Applications from '../applicationsTab/Applications';
function SideBar() {
const selector = useSelector((state) => state.selector);
const dispatch = useDispatch();
console.log(selector);
return (
<div className="Sidebar">
{/* logo */}
<div className="logo">
<img src={Logo} alt="logo" />
<span>Company Name</span>
</div>
{/* menu */}
<div className="menu">
{SidebarData.map((item, index) => (
/* eslint-disable */
<div
role="menu"
className={selector === index ? 'menuItem active' : 'menuItem'}
key={index}
onClick={() => dispatch(setSelector(selector))}
onKeyDown={() => dispatch(setSelector(selector))}
>
<item.icon />
<span>{item.heading}</span>
</div>
))}
<div className="menuItem">
<UilSignOutAlt />
</div>
</div>
</div>
);
}
export default SideBar;
TL;DR:
When clicking on the dashboard "Application" change the state in redux for another component as well to re-render the main tab.
Thanks guys! Do need to understand what I'm doing wrong...
I try to create a navbar which later will change after logging in based on user role, but after I tried it the navbar was successfully changed after login or logout, BUT if I should refresh the browser. so if i don't refresh the browser then the navbar won't change. how to solve it? here's the code of app.js
import React from 'react';
import './App.css';
import 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import NavbarAwal from './Component/NavbarAwal';
import jwt_decode from 'jwt-decode';
import 'react-bootstrap';
import NavbarPembeli from './Component/NavbarUser';
import NavbarPenjual from './Component/NavbarPenjual';
import { login, register } from './View/UserFunctions';
import Routes from './Router';
class App extends React.Component {
constructor() {
super();
this.state = {
Roles: "",
};
}
render() {
const token=localStorage.getItem('usertoken');
if(token===null){
return (
<div id="root">
<NavbarAwal/>
<Routes/>
</div>
)
}
const decoded = jwt_decode(token);
if (decoded.Roles === "Penjual") {
return (
<div id="root">
<NavbarPenjual/>
<Routes/>
</div>
)
} else if (decoded.Roles === "Pembeli") {
return (
<div id="root">
<NavbarPembeli/>
<Routes/>
</div>
)
} else {
return (
<div>
<NavbarAwal/>
<Routes/>
</div>
)
}
}
}
export default App;
I want to implement the navigation on menu bar which I am fetching from api. For e.g. on home page I have four menus like menu1 menu2 menu3 menu4 which displays always. On click on these menus i want to fetch products related to them.
I have read about nested routes in React js but unable to implement that.
Dynamic menu bar of categories:
import React from 'react';
import './style.css';
import {Link} from 'react-router-dom';
import Api from '../../Api';
class TopMenu extends React.Component {
state = {
categories : []
}
componentDidMount(){
Api.get(`categories`).then(
response => {
this.setState({categories: response.data});
});
};
render(){
return (
<div className="menu">
{this.state.categories.map(category => (
<Link to={"/category/" + category.name} key={category.id} className="menu-item"><span>{category.name}</span></Link>
))}
</div>
);
}
};
export default TopMenu;
My Routes file:
import React from 'react';
import {Switch, Route} from 'react-router-dom';
import CategoryProducts from './CategoryProducts';
import Home from './Home';
const Routes = () => {
return(
<Switch>
<Route path='/' exact component={Home} />
<Route path='/category/:name' component={CategoryProducts} />
</Switch>
);
};
export default Routes;
The click on Category will just change the browser url, not the page.
CategoryProducts.ja
import React from 'react';
import Products from './Products';
class CategoryProducts extends React.Component {
render(){
return (
<div className="content-wrapper">
<div className="menu-left">
<Products/>
</div>
</div>
);
}
}
export default CategoryProducts;
Products.js
import React,{useState, useEffect} from 'react';
import Api from './Api'
import Card from './components/Card';
class Products extends React.Component {
state = {
categories : []
}
componentDidMount(){
let categoryName = this.props.match ? this.props.match.params.name : 'Veg Pizza';
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
};
render(){
return (
<div>
<div className="ref">
<div className="menu-hr"></div>
<div className="menu-cat">
<div className="menu-catname ">BESTSELLERS</div>
</div>
</div>
<div className="card-container">
<div className="all-cards" data-label="Bestsellers">
<Card />
</div>
</div>
</div>
);
}
};
export default Products;
Your Products component is not mounting again and again, because this renders on all possible categories. Therefore In order to fetch data for different categories, you might have to use componentDidUpdate lifecycle method.
import React,{useState, useEffect} from 'react';
import Api from './Api'
import Card from './components/Card';
class Products extends React.Component {
state = {
categories : []
}
componentDidMount(){
let categoryName = this.props.match ? this.props.match.params.name : 'Veg Pizza';
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
};
componentDidUpdate(prevProps, prevState){
if(prevProps.match.params.name !== this.props.match.params.name){
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
}
}
render(){
return (
<div>
<div className="ref">
<div className="menu-hr"></div>
<div className="menu-cat">
<div className="menu-catname ">BESTSELLERS</div>
</div>
</div>
<div className="card-container">
<div className="all-cards" data-label="Bestsellers">
<Card />
</div>
</div>
</div>
);
}
};
export default Products;
If you want to force rerenders anyway and componentDidUpdate doesnt works for you, you can cause force rerender using key prop
import React from 'react';
import Products from './Products';
class CategoryProducts extends React.Component {
render(){
return (
<div className="content-wrapper">
<div className="menu-left">
<Products key={this.props.match.params.name}/>
</div>
</div>
);
}
}
export default CategoryProducts;
Please let me know if ut didnt solve your problem.
I'm getting props.handleChange is not a function when running the following code. I'm trying to update the state when the checkbox is clicked. The field that is check box is called myNetwork. I thought that when NetworkArray component, which is a parent of Card component, would have access to the functions and state in App? But this is my first React App. Please, what am I doing wrong?
App.JS
import React, {Component} from 'react';
import SignUp from './components/SignUp';
import NetworkArray from './components/NetworkArray';
import {network} from './NetworkData'
import './App.css';
import 'tachyons';
class App extends Component {
constructor() {
super()
this.state = {
network: network,
}
this.handleChange=this.handleChange.bind(this);
}
handleChange(id) {
this.setState(prevState => {
const updatedNetwork = prevState.network.map(netw => {
if (netw.id===id) {
netw.myNetwork = !netw.myNetwork
}
return netw
})
return {
network:updatedNetwork
}
})
}
render() {
return (
<div>
<NetworkArray
network={network}
handleChange = {this.handleChange} />
</div>
);
}
}
export default App;
Card.js
import React from 'react';
const Card = (props) => {
return(
<div className = 'bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<div>
<h3>{props.name}</h3>
<p>{props.company}</p>
<p>{props.phone}</p>
<p>{props.email}</p>
<p>{props.city}</p>
</div>
<div>
MyNetwork
<input
type = "checkbox"
checked={props.myNetwork}
onChange={()=> props.handleChange(props.id)}
/>
</div>
</div>
)
}
export default Card;
NetworkArray.js
import React, {Component} from 'react';
import Card from './Card';
const NetworkArray = ({network}) => {
const cardComponent = network.map((user,i) => {
return(
<Card
key = {network[i].id}
name = {network[i].firstName + ' ' + network[i].lastName}
company = {network[i].company}
phone= {network[i].phone}
email={network[i].email}
city = {network[i].city}
/>
)
})
return (
<div>
{cardComponent}
</div>
)
}
export default NetworkArray;
You passed the function from App component to NetworkArray component, but not to Card component.
const NetworkArray = ({network, handleChange}) => {
...
<Card
handleChange={handleChange}
...
/>
}
I'm new to react + redux. I encountered a problem which my app is not re-render when I dispatch an action. However, I use getState() to examine the state, it did change. I look up documents but still have no idea what the problem is. Please help me, thanks.
The code is as below
====actions.js====
export const ADD_MAIL = 'ADD_MAIL';
export const DEL_MAIL = 'DEL_MAIL';
export function addMail(email) {
return {
type: ADD_MAIL,
email
}
}
export function delMail(id) {
return {
type: DEL_MAIL,
id
}
}
====reducers.js====
import { combineReducers } from 'redux'
import { ADD_MAIL, DEL_MAIL } from '../actions/actions'
import MAILS from '../data'
function emails(state = MAILS, action) {
switch (action.type) {
case ADD_MAIL:
console.log("ADD_MAIL");
return [
action.email,
...state
];
case DEL_MAIL:
let idx = state.length;
let i = 0;
// Find the target mail
while(idx--) {
if (state[idx] && state[idx].serialNo === action.id)
i = idx;
}
let arr1 = state.slice(0, i);
let arr2 = state.slice(i + 1);
let newList = arr1.concat(arr2);
console.log("DEL_MAIL");
return newList;
default:
return state;
}
}
const rootReducer = combineReducers({
emails
});
export default rootReducer;
====main.js====
import React from 'react'
import { render } from 'react-dom'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import { createStore } from 'redux'
import { addMail, delMail } from './actions/actions'
import rootReducer from './reducers/reducers'
import * as btn from './module/button'
import * as module from './module/module'
var store = createStore(rootReducer);
class Inbox extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: ''
}
this.handleUserInput = this.handleUserInput.bind(this);
this.deleteMail = this.deleteMail.bind(this);
this.sendMail = this.sendMail.bind(this);
}
handleUserInput(searchText) {
this.setState({
searchText: searchText
});
}
deleteMail(obj) {
store.dispatch(delMail(obj.serialNo));
console.log(store.getState());
// This displays the correct new state in console after dispatch
}
sendMail(newMail) {
store.dispatch(addMail(newMail));
console.log(store.getState());
// This displays the correct new state in console after dispatch
}
render() {
let mails = [];
let search = this.state.searchText.toUpperCase();
let emails = this.props.emails;
emails.map(mail => {
if (mail.from.toUpperCase().indexOf(search) !== -1)
mails.push(mail);
});
let sender = (mails.length === emails.length) ? "all" : this.state.searchText;
return (
<div className="main">
<div className="toolbar">
<span>You have {mails.length} message from {sender}.</span>
<module.SearchInput searchText={this.state.searchText} onUserInput={this.handleUserInput} />
<div className="functions">
<btn.AddButton />
</div>
</div>
<div className="mailList">
{mails.map(mail => (
<div className="item" key={mail.serialNo}>
<div className="info sender">
From: {mail.from}
</div>
<div className="info date">
{mail.date}
</div>
<div className="info subject">
Subject: {mail.subject}
</div>
<div className="functions">
<btn.ReadButton serialNo={mail.serialNo} />
<btn.DeleteButton serialNo={mail.serialNo} deleteMail={this.deleteMail} />
</div>
</div>
))}
</div>
<module.NewMailInput sendMail={this.sendMail} />
</div>
);
}
}
function mapStateToProps(state) {
return {
emails: state.emails
};
}
export default connect(mapStateToProps)(Inbox);
====app.js====
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import { Menu } from './menu'
import { Mail } from './main'
import Inbox from './main'
import rootReducer from './reducers/reducers'
var store = createStore(rootReducer);
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div style={{height: '100%'}}>
<Menu />
{this.props.children}
</div>
);
}
}
class Home extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Inbox />
);
}
}
render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="/inbox" component={Inbox} />
<Route path="/message/:mailSerial" component={Mail} />
</Route>
</Router>
</Provider>,
document.getElementById('app-container'))
Try this:
import React from 'react'
import { render } from 'react-dom'
import { Link } from 'react-router'
import { connect } from 'react-redux'
import { addMail, delMail } from './actions/actions'
import * as btn from './module/button'
import * as module from './module/module'
class Inbox extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: ''
}
this.handleUserInput = this.handleUserInput.bind(this);
this.deleteMail = this.deleteMail.bind(this);
this.sendMail = this.sendMail.bind(this);
}
handleUserInput(searchText) {
this.setState({
searchText: searchText
});
}
deleteMail(obj) {
this.props.delMail(obj.serialNo); //Call the delMail action
console.log(store.getState());
}
sendMail(newMail) {
this.props.addMail(newMail); //Call the addMail action
console.log(store.getState());
}
render() {
let mails = [];
let search = this.state.searchText.toUpperCase();
let emails = this.props.emails;
emails.map(mail => {
if (mail.from.toUpperCase().indexOf(search) !== -1)
mails.push(mail);
});
let sender = (mails.length === emails.length) ? "all" : this.state.searchText;
return (
<div className="main">
<div className="toolbar">
<span>You have {mails.length} message from {sender}.</span>
<module.SearchInput searchText={this.state.searchText} onUserInput={this.handleUserInput} />
<div className="functions">
<btn.AddButton />
</div>
</div>
<div className="mailList">
{
mails.map(mail => (
<div className="item" key={mail.serialNo}>
<div className="info sender">From: {mail.from}</div>
<div className="info date">{mail.date}</div>
<div className="info subject">Subject: {mail.subject}</div>
<div className="functions">
<btn.ReadButton serialNo={mail.serialNo} />
<btn.DeleteButton serialNo={mail.serialNo} deleteMail={this.deleteMail} />
</div>
</div>
))
}
</div>
<module.NewMailInput sendMail={this.sendMail} />
</div>
);
}
}
function mapStateToProps(state) {
return {
emails: state.emails
};
}
//Connect the component to re-render when state change and
// makes the emails and actions to be available through this.props
export default connect(mapStateToProps, {delMail, addMail})(Inbox);
//To connect Mail component which I suppose that is in another file
function mapStateToProps(state) {
return { emails: state.emails };
}
export default connect(mapStateToProps, {})(Mail);
In your main.js file, you have made a Inbox component. That is a React Component but not a Redux Component.
You have to do something like this while exporting Inbox component.
module.exports = connect((store)=> {
return {emails: store.emails}
})(Inbox)
You have 2x stores: one in main.js and one in app.js. Remove the one in main.js and update the calls to dispatch to use the one passed as props:
class Inbox extends React.Component {
...
deleteMail(obj) {
this.props.dispatch(delMail(obj.serialNo));
}
...
}