I am new to react and I can't debug why mapStateToProps is not running. Pls see the last function in login.js.
I have added alert statements in my mapStateToProps function but its just not running. Dont know where to look for problems.
store.js
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
export const store = createStore(
rootReducer,
applyMiddleware(thunkMiddleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
index.js:
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './helpers';
import { App } from './App';
import { configureFakeAPI } from './helpers';
configureFakeAPI();
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
App.js
import React from 'react';
import { Router, Route } from 'react-router-dom';
import { connect } from 'react-redux';
import { PrivateRoute } from './PrivateRoute.js';
import { history } from './helpers';
import { alertActions } from './actions';
import { HomePage } from './components/HomePage';
import LoginPage from './components/LoginPage';
import { RegisterPage } from './components/RegisterPage';
export class App extends React.Component {
constructor(props) {
super(props);
const { dispatch } = this.props;
history.listen((location, action) => {
});
}
render() {
const { alert } = this.props;
return (
<div className="container">
<div className="col-sm-8 col-sm-offset-2">
<LoginPage />
</div>
</div>
);
}
}
function mapStateToProps(state) {
const { alert } = state;
return {
alert
};
}
export default connect(mapStateToProps)(App);
LoginPage.js
import React from 'react';
import {Component} from 'react';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import {userActions} from '../actions';
import {userConstants} from "../constants";
class LoginPage extends Component {
constructor(props) {
super(props);
// reset login status
this.state = {
username: '',
password: '',
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = (e) => {
let formControlName = e.target.name;
let value = e.target.value;
this.setState({
[formControlName]: value
});
};
handleSubmit = (e) => {
e.preventDefault();
this.sendTheAlert();
}
render() {
const {username, password, submitted} = this.state;
return (
<div className="col-md-6 col-md-offset-3">
<i>{JSON.stringify(this.state)}</i>
<h2>Login</h2>
<form name="form" onSubmit={this.handleSubmit}>
<div className={'form-group' + (submitted && !username ? ' has-error' : '')}>
<label htmlFor="username">Username</label>
<input type="text" className="form-control username" name="username"
onChange={this.handleChange}/>
{submitted && !username &&
<div className="help-block">Username is required</div>
}
</div>
<div className={'form-group' + (submitted && !password ? ' has-error' : '')}>
<label htmlFor="password">Password</label>
<input type="password" className="form-control" name="password" onChange={this.handleChange}/>
{submitted && !password &&
<div className="help-block">Password is required</div>
}
</div>
<div className="form-group">
<button className="btn btn-primary">Login</button>
<a className="btn btn-link">Register</a>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state) {
// const { todos } = state;
// return { todoList: todos.allIds };
return {};
}
// function mapDispatchToProps(dispatch) {
// alert();
// return ({
// sendTheAlert: () => {
// dispatch(userConstants.LOGIN_REQUEST)
// }
// })
// }
const mapDispatchToProps = (dispatch) => ({ <====== NOT RUNNING
sendTheAlert(coin) {
debugger;
alert();
dispatch(userConstants.LOGIN_REQUEST) }
})
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);
I assume that it is mapDispatchToProps that isnt working, right?
Try this
...
const mapDispatchToProps = (dispatch) => (
return {
sendTheAlert(coin) {
debugger;
alert();
return dispatch(userConstants.LOGIN_REQUEST) }
})
A sample of how to structure mapDispatchToProps would be (from https://react-redux.js.org/using-react-redux/connect-mapdispatch). Be mindful of the return statement.
const mapDispatchToProps = dispatch => {
return {
// dispatching plain actions
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' })
}
}
Everything is fine I was just calling this.sendTheAlert() wrong. it should be this.props.sendTheAlert()
Related
I am trying to make a simple exercise for react-redux to understand the process but somehow I am stuck .. any help would be really appreciate.
The interesting part is when I do subscribe and try to log store into the console, it works and shows me updated value but I am not able to select it using useSelector
Also with the help of Dev tool's i could see the state being changed from INIT to ADD_USER..
Below are my components files and reducers.
App.js
import React from "react";
import { Provider } from "react-redux";
import store from "./stores/store";
import { HomePage } from "./components/containers/HomePage";
function App() {
return (
<Provider store={ store }>
<HomePage/>
</Provider>
);
}
export default App;
HomePage.js. --> Here state.isLogin is not selected.. but the subscribe comment works
import React from "react";
import { Sidebar } from "./Sidebar";
import { LoginPage } from "./LoginPage";
import { useSelector } from "react-redux";
export const HomePage = () => {
const userLogin = useSelector(state => state.isLogin);
// const storeState = store.subscribe (() => console.log(store.getState()));
return (
<div>
<LoginPage />
<Sidebar />
</div>
);
};
LoginPage.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import * as action from "../../action/index";
export const LoginPage = (setLogin) => {
const dispatch = useDispatch();
const [name, setName] = useState("");
const createUser = (e) => {
e.preventDefault();
const addUser = {
name: name,
isLogin: true
};
dispatch(action.addUsers(addUser));
};
return (
<div className="card border-0 shadow">
<div className="card-header">Login Here!</div>
<div className="card-body">
<form onSubmit={(e) => createUser(e)}>
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Enter Your Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<button className="btn btn-primary" type="submit">
Create Contact
</button>
</form>
</div>
</div>
);
};
reducers - Index.js and user.js
import userReducer from './users'
import { combineReducers} from "redux";
const allReducers = combineReducers({
addUser : userReducer,
});
export default allReducers;
User.js
import * as types from '../actionTypes/index'
const intialState = {
user: [],
messages : [],
isLogin : false
};
const users = (state = intialState, action) => {
switch (action.type) {
case types.ADD_USER:
return {
...state,
user: [action.payload.name, ...state.user],
isLogin: action.payload.isLogin
};
default:
return state
}
}
export default users;
Store.js
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import allReducers from '../reducers'
const store = createStore(allReducers,composeWithDevTools());
export default store;
Any idea's/Input on what went wrong ? or what is the issue?
Thank you
Try using below code in HomePage
const userLogin = useSelector(state => state.addUser.isLogin);
How can I access the field values in a parent component of a redux-form component?
I'm not sure if it's caused by typescript, but before I started using typescript, I was able to access the form values through mapStateToProps just like how I have it currently. I've been trying to figure out what was different to my previous implementation but the only difference would be the versions of the npm dependencies and the addition of typescript.
LoginPage.tsx
import LoginForm from 'components/Forms/LoginForm'
import Layout from 'components/Layout'
import { StatusCodes } from 'lib/enums/statusCodes'
import { storeAuthToken } from 'lib/helpers/auth'
import { NextPage } from 'next'
import Router from 'next/router'
import React from 'react'
import { connect, DispatchProp } from 'react-redux'
import { FormInstance } from 'redux-form'
interface IProps {
login: FormInstance<IFormData, IFormProps>
}
interface IState {
errorMessage?: string,
processing: boolean
}
interface IRootState {
form: IProps
}
export interface IFormData {
username?: string,
password?: string
}
export interface IFormProps {
contactId?: string,
errorMessage?: string,
fieldValues: Partial<IFormData>,
processing: boolean
}
class LoginPage extends React.Component<NextPage & DispatchProp & IProps, IState> {
state = {
errorMessage: undefined,
processing: false
}
setErrorMessage = (message: string) => {
this.setState({
errorMessage: message,
processing: false
})
}
handleSubmit = async (values: IFormData) => {
if (values && values.username && values.password) {
this.setState({
errorMessage: undefined,
processing: true
})
try {
const { dispatch } = this.props
await storeAuthToken(dispatch, values.username, values.password)
Router.push('/')
} catch (error) {
if (error === StatusCodes.BAD_REQUEST) {
this.setErrorMessage("Sorry, you have entered incorrect details. Please try again.")
} else {
this.setErrorMessage("Sorry, there was an issue trying to log you in")
}
}
}
}
render() {
const { login } = this.props
const { processing } = this.state
return (
<Layout title="Login">
<div className="form-wrapper full">
<LoginForm processing={processing} onSubmit={this.handleSubmit} fieldValues={login.values} />
</div>
</Layout>
)
}
}
const mapStateToProps = ({ form: { login } }: IRootState) => ({ login })
export default connect(mapStateToProps)(LoginPage)
LoginForm.tsx
import Link from 'next/link'
import React from 'react'
import { Field, InjectedFormProps, reduxForm } from 'redux-form'
import FormButton from 'components/Forms/FormButton'
import Input from 'components/Fields/Input'
import { validateRequired } from 'lib/helpers/validators'
import { IFormProps, IFormData } from 'pages/login'
class LoginForm extends React.Component<IFormProps & InjectedFormProps<IFormData, IFormProps>> {
render() {
const { contactId, errorMessage, fieldValues, handleSubmit, processing } = this.props
return (
<form id="login" onSubmit={handleSubmit} >
<h1>Sign in</h1>
<fieldset>
<div className="fields">
{
!contactId
? <Field name="username" type="text" component={Input} label="Username" validate={validateRequired} />
: <Field name="username" type="email" component={Input} label="Email" validate={validateRequired} />
}
</div>
<div className="fields">
<Field name="password" type="password" component={Input} label="Password" validate={validateRequired} />
</div>
</fieldset>
{ errorMessage && <p className="error-message">{errorMessage}</p> }
<div className="form-bottom">
<Link href="/"/*{`/forgot-password${fields.email ? `?email=${encodeURIComponent(fields.email)}` : ''}`}*/>
<a className="inline">Forgotten your password?</a>
</Link>
<FormButton loading={processing}>
Login
</FormButton>
</div>
</form>
)
}
}
export default reduxForm<{}, IFormProps>({ form: 'login' })(LoginForm)
Here is my redux store file incase if that is coded incorrectly
import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'
export interface State {
auth: AuthState
contact: ContactState
}
const bindMiddleware = (middleware: [ThunkMiddleware]) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const combinedReducer = combineReducers({
auth: authReducer,
contact: contactReducer,
form: formReducer
})
const reducer: Reducer = (state: State, action: AnyAction) => {
if (action.type === HYDRATE) {
const nextState: Reducer = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer
}
}
const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))
export const wrapper = createWrapper<State>(makeStore/*, { debug: true }*/)
It seems like I missed out a key in the IApplicationState interface and as mentioned by #cbr, the parameters state and action needed to be passed to combinedReducer even though it doesn't directly take any.
Additionally it didn't like when the nextState constant had the type Reducer, so I have changed that to CombinedState<State> as well
The changed code looks like this
import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
import { AnyAction, applyMiddleware, combineReducers, createStore, Reducer } from 'redux'
import { reducer as formReducer } from 'redux-form'
import thunkMiddleware, { ThunkMiddleware } from 'redux-thunk'
import authReducer, { AuthState } from './auth/reducer'
import contactReducer, { ContactState } from './contact/reducer'
import initialState from './initialState'
export interface State {
auth: AuthState
contact: ContactState,
form: FormStateMap
}
const bindMiddleware = (middleware: [ThunkMiddleware]) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const combinedReducer = combineReducers({
auth: authReducer,
contact: contactReducer,
form: formReducer
})
const reducer: Reducer = (state: State, action: AnyAction) => {
if (action.type === HYDRATE) {
const nextState: CombinedState<State> = {
...state,
...action.payload
}
return nextState
} else {
return combinedReducer(state, action)
}
}
const makeStore: MakeStore<State> = () => createStore(reducer, initialState, bindMiddleware([thunkMiddleware]))
export const wrapper = createWrapper<State>(makeStore)
These are my classes and component:
App.js:
import { connect } from "react-redux";
import Main from "./components/main/Main";
import * as actions from "./redux/actions";
import { bindActionCreators } from "redux";
import { withRouter } from "react-router";
function mapStateToProps(state) {
console.log(state);
return {
// naming must be exact same as function in reducer
auth: state.auth,
errorBag: state.errorBag
};
}
function mapDispatchProps(dispatch) {
return bindActionCreators(actions, dispatch);
}
const App = withRouter(connect(mapStateToProps, mapDispatchProps)(Main));
export default App;
Main.js:
import React, { Component } from "react";
import { Redirect } from "react-router";
import { Route } from "react-router-dom";
import Login from "./../login/Login";
import Home from "./../home/Home";
class Main extends Component {
render() {
return (
<div>
<Route
path="/login"
render={param => {
return <Login {...this.props} {...param} />;
}}
/>
{!this.auth ? <Redirect to="/login" /> : <Home />}
</div>
);
}
}
export default Main;
reducer.js:
import { combineReducers } from "redux";
function authenticateReducer(state = null, action) {
switch (action.type) {
case "AUTHENTICATE": {
return action.token;
}
default: {
return state;
}
}
}
function errorBagReducer(state = [], action) {
switch (action.type) {
case "ADD_ERROR": {
console.log("called");
return { [action.area]: action.error };
}
default:
return state;
}
}
const rootReducer = combineReducers({ authenticateReducer, errorBagReducer });
export default rootReducer;
actions.js:
import axios from "axios";
export function startSignUp(signUpData) {
return dispatch => {
return axios
.post("http://localhost:8000/api/v1/user/sign-up/", signUpData)
.then(res => {
console.log(res);
authenticate(res.data.shaba);
})
.catch(error => {
console.log(error.data);
});
};
}
export function addErrorIntoBag(area, error) {
return {
type: "ADD_ERROR",
area,
error
};
}
function authenticate(token) {
return {
type: "AUTHENTICATE",
token
};
}
Login.js:
import React, { Component } from "react";
import "./login.css";
import InlineAlert from "./../alerts/InlineAlert";
class Login extends Component {
constructor(props) {
super(props);
}
getAreaName = () => "LOGIN";
submitHandler = event => {
event.preventDefault();
const email = event.target.elements.email.value;
const code = event.target.elements.code.value;
const password = event.target.elements.password.value;
if (password === "" || code === "" || email === "") {
this.props.addErrorIntoBag(this.getAreaName(), "fill the form please");
return;
}
const data = {
email: email,
password: password,
national_code: code
};
this.props.startSignUp(data);
this.forceUpdate();
};
render() {
return (
<div id="parent" onSubmit={this.submitHandler}>
<form id="form_login">
<h2>Login</h2>
<br />
<div className="form-group">
<label forhtml="exampleInputEmail1">National code</label>
<input
key="code"
type="text"
className="form-control"
id="exampleInputEmail1"
aria-describedby="emailHelp"
name="code"
></input>
</div>
<div className="form-group">
<label forhtml="exampleInputPassword1">Email</label>
<input
type="email"
className="form-control"
id="email"
name="email"
key="email"
></input>
</div>
<div className="form-group">
<label forhtml="exampleInputPassword1">Password</label>
<input
type="password"
className="form-control"
id="name"
name="password"
key="password"
></input>
</div>
<div className="form-group form-check"></div>
<button type="submit" className="btn btn-light">
SIGN-UP
</button>
</form>
{/* drill down checking 🤯 */}
{this.props.errorBag && this.props.errorBag[this.getAreaName()] && (
<InlineAlert error={this.props.errorBag[this.getAreaName()]} />
)}
</div>
);
}
}
export default Login;
When I call submitHandler in Login.js and form is empty it calls an action to changes the redux store and Login.js component must update and InlieAlert component must appear in the screen, but anything doesn't change!
Is it because of my Main.js that is middle of Login and Redux?
I'm confused!
I guess you forgot to call dispatch in startSignUp function. It is not enough just to return from authenticate the object as you did with { type: "AUTHENTICATE", token } but you need to pass to dispatch also. In this way you are triggering a state change.
From the documentation of dispatch(action):
Dispatches an action. This is the only way to trigger a state change.
Maybe you could try to do somehow the following:
export function startSignUp(signUpData) {
return dispatch => {
return axios
.post("http://localhost:8000/api/v1/user/sign-up/", signUpData)
.then(res => {
console.log(res);
// missing dispatch added
dispatch(authenticate(res.data.shaba));
})
.catch(error => {
console.log(error.data);
});
};
}
So you can think of like calling as the following at the end for the state change:
dispatch({
type: "AUTHENTICATE",
token
});
I hope this helps!
i am learning enzyme with react but i am facing this problem can any one help me out . i am trying to test register page on change username and password but i having this issue
//RegisterPage.js
import React from 'react'
import { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../actions';
export class RegisterPage extends Component {
constructor(props) {
super(props);
this.state = {
user: {
username: '',
password: ''
},
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
}
});
}
handleSubmit(event) {
event.preventDefault();
// handle button click and dispatch register
const { user } = this.state;
if (user.username && user.password) {
this.props.dispatch(userActions.register(user))
}else{
this.setState({submitted:true})
}
}
render() {
const { user, submitted } = this.state;
const { registering } = this.props;
return (
<div className="col-md-6 col-md-offset-3">
<h2>Register</h2>
<form name="form" onSubmit={this.handleSubmit}>
<div className={'form-group' + (submitted && !user.username ? ' has-error' : '')}>
<label htmlFor="username">Username</label>
<input value={user.username} onChange={this.handleChange} type="text" className="form-control username" name="username" />
{submitted && !user.username &&
<div className="help-block">Username is required</div>
}
</div>
<div className={'form-group' + (submitted && !user.password ? ' has-error' : '')}>
<label htmlFor="password">Password</label>
<input value={user.password} onChange={this.handleChange} type="password" className="form-control" name="password"/>
{submitted && !user.password &&
<div className="help-block">Password is required</div>
}
</div>
<div className="form-group">
<button className="btn btn-primary">Register</button>
<Link to="/login" className="btn btn-link">Cancel</Link>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
...state
}
RegisterPage=connect(mapStateToProps)(RegisterPage)
export default RegisterPage;
here is my test case which i have written i am new on enzyme so no idea about this how this will work
//RegisterPage.test.jsenter image description here
import React from 'react';
import ReactDOM from 'react-dom';
import expect from 'expect';
import TestRegisterPage from '../components/RegisterPage';
import { App } from '../App';
import { configure } from 'enzyme';
import { Link } from 'react-router-dom';
import { MemoryRouter } from 'react-router'
import Adapter from 'enzyme-adapter-react-16';
import { mount, shallow } from 'enzyme';
import { PropTypes } from 'prop-types';
import renderer from 'react-test-renderer';
import {spy} from 'sinon';
import { Provider } from 'react-redux';
import { store } from '../helpers';
import configureStore from 'redux-mock-store';
const middlewares = [];
const mockStore = configureStore(middlewares);
configure({ adapter: new Adapter() });
let mockedStore = mockStore({});
export const CustomProvider = ({ children }) => {
return (
<Provider store={store}>
<MemoryRouter>
{children}
</MemoryRouter>
</Provider>
);
};
describe("Test RegisterPage component", () => {
it('check route, dispatch and store setup for component', () => {
const wrapper = mount(
<CustomProvider>
<TestRegisterPage dispatch={spy()} />
</CustomProvider>
{
context: {store: mockedStore},
childContextTypes: {store: PropTypes.object.isRequired}
}
);
const inst = wrapper.children();
let uname = inst.find('.username');
let pwd = inst.find('.password');
uname.simulate('change', { target: { value: "demo_username" } });
pwd.simulate('change', { target: { value: "demo_password" } });
wrapper.update();
expect(wrapper).toBeTruthy();
});
});
You can get the input via name selectors, without getting the children:
let uname = wrapper.find('input[name="username"]');
let pwd = wrapper.find('input[name="password"]');
uname.simulate('change',{ target: {value: "demo_username"} });
pwd.simulate('change', { target: { value: "demo_password" } });
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));
}
...
}