I am going to post my question here and hopefully learn something! I've been following a tutorial and do not yet have the insight into React and Redux to effectively figure out what is going on. I want to create functionality that adds and removes businesses to global state in React and Redux. From what I've Googled I know the file structures vary depending on the project so I will post all of my files here. I have them divided into actions.js, reducers.js, state.js, and store.js. I have an add Listing view with React and would like to add remove functionality to my view listings view. Here are my files:
redux/actions.js:
export const addListing = (newBusiness) => {
return {
type: 'ADD_LISTING',
value: newBusiness
}
}
export const removeListing = (index) => {
return {
type: 'REMOVE_LISTING',
value: index
}
}
redux/reducers.js:
import { combineReducers } from 'redux';
import { addBusiness, removeBusiness } from './actions'
const user = (state = null) => state
// add switch statements in here
const businesses = (state = [], action) => {
switch(action.type) {
case 'ADD_BUSINESS':
return [ ...state, action.value ]
case 'REMOVE_BUSINESS':
const newState = [ ...state ]
newState.splice(action.value, 1);
return newState;
default: // need this for default case
return state
}
}
export default combineReducers({ user, businesses })
redux/state.js
export default {
user: {
username: 'test-user-1',
email: 'test-user#example.com'
},
businesses: [
{
"id": 15,
"name": "My pizza",
"description":"Homemade pizza shop",
"address": "123 Taco Street",
"hours": "9-5"
}
]
};
redux/store.js
import { createStore } from 'redux'
import reducers from './reducers'
import state from './state'
export default createStore(reducers, state)
containers/addListing.js
import { connect } from "react-redux";
import { addListing } from '../redux/actions';
import AddListing from '../components/addListing'
const mapDispatchToProps = (dispatch) => {
return {
addListing: (business) => dispatch(addListing(business)),
}
}
export default connect(null, mapDispatchToProps)(AddListing)
containers/removeListing.js
import { connect } from "react-redux";
import { removeListing } from '../redux/actions';
const mapDispatchToProps = (dispatch) => {
return {
removeCar: (business) => dispatch(removeListing(business)),
}
}
export default connect(null, mapDispatchToProps)(removeListing)
containers/Listing.js:
import { connect } from 'react-redux'
import Listing from '../components/Listing'
const mapStateToProps = (state) => {
return {
businesses: state.businesses,
user: state.user.username
}
}
export default connect(mapStateToProps)(Listing)
components/addListing.js
import React from 'react';
import { InputLabel } from '#material-ui/core'
import { Input } from '#material-ui/core'
import { FormControl } from '#material-ui/core';
import { Button } from '#material-ui/core';
import { TextField } from '#material-ui/core';
import '../redux/state';
class addListing extends React.Component {
state = {
name: '',
description: '',
address: '',
hours: ''
}
handleTextChange = (e) => {
const newState = { ...this.state }
newState[e.target.id] = e.target.value
this.setState(newState)
console.log(this.state)
}
handleSubmit = (e) => {
e.preventDefault()
const payload = { ...this.state }
console.log("THE BUSINESS", payload)
this.props.addListing(payload)
console.log(this.props)
}
componentDidUpdate = (prevProps, prevState) => {
if (prevState.open !== this.state.open) {
this.setState({
name: '',
description: '',
address: '',
hours: ''
})
}
}
render(){
return (
<div className="App">
<form
onSubmit={this.handleSubmit}
style={{ display: 'flex', flexDirection: 'column', width: '350px' }}>
<TextField
id="name"
name="name"
placeholder="Name"
value={this.state.name}
onChange={this.handleTextChange}
required />
<TextField
id="description"
name="description"
placeholder="Description"
value={this.state.description}
onChange={this.handleTextChange}
required />
<TextField
id="address"
name="address"
placeholder="Address"
value={this.state.address}
onChange={this.handleTextChange}
required />
<TextField
id="hours"
name="hours"
placeholder="Hours"
value={this.state.hours}
onChange={this.handleTextChange}
required />
<br />
<Button variant="contained" color="primary" type="submit">Submit</Button>
</form>
</div>
);
}
}
export default addListing;
components/Listing.js:
import React from 'react'
import {
Container,
Table,
TableBody,
TableCell,
TableHead,
TableRow
} from '#material-ui/core'
import DeleteIcon from '#material-ui/icons/Delete'
import addListing from '../containers/addListing'
import removeListing from '../containers/removeListing'
import businesses from '../redux/state'
import user from '../redux/state';
const Listing = (props) => {
console.log(props.businesses)
return (
<Container maxWidth="lg" className="car-container">
<h4>Welcome, {props.user.username}</h4>
<div className="flex-container">
</div>
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Name</TableCell>
<TableCell>Description</TableCell>
<TableCell>Address</TableCell>
<TableCell>Hours</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.businesses.map((businesses, idx) => (
<TableRow key={businesses.id}>
<TableCell component="th" scope="row">
</TableCell>
<TableCell>{businesses["name"]}</TableCell>
<TableCell>{businesses["description"]}</TableCell>
<TableCell>{businesses["address"]}</TableCell>
<TableCell>{businesses["hours"]}</TableCell>
<TableCell>
<DeleteIcon
// add onClick method here
// onClick={props.removeCar(idx)}
className="icon text-red"
onClick={ () => this.props.removeListing(idx)}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Container>
)
}
export default Listing
App.js:
import React from 'react';
import Navigation from './components/Navigation'
import './App.css'
import Router from './Router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './redux/store'
function App() {
return (
<Provider store={store}>
<BrowserRouter>
<Navigation />
<Router />
</BrowserRouter>
</Provider>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Login from './components/Login'
import App from './App';
import * as serviceWorker from './serviceWorker';
import Listing from '../src/components/Listing';
import { Provider } from 'react-redux';
ReactDOM.render(
<App />,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
So far I know it's something simple. I am getting no errors but it just doesn't seem to add the new listings to global state. When I get to the listing view it only displays the Business I included as a default in state.js. I will try to reply in a quick manner and please let me know if more info is needed. Thanks!
I see that the names of your actions are different ADD_LISTING vs ADD_BUSINESS, REMOVE_LISTING vs. REMOVE_BUSINESS.
In the addListing you have {type: 'ADD_LISTING', ...}, in your reducer you have case 'ADD_BUSINESS': The strings are different. They need to match. Try renaming ADD_BUSINESS => ADD_LISTING, and REMOVE_BUSINESS=>REMOVE_LISTING.
Regarding the crash on line 50. You don't need this. because your component is not a class type. Change it to onClick={ () => props.removeListing(idx)}.
You're missing removeListing in the mapDispatchToProps.
Also, Redux DevTools plugin for Chrome can help you a lot in debugging issues with redux..
Related
I am writing a test for a component that is wrapped in a withStyles() from Material UI using Jest. I have searched many examples but didn't get solution.
Below are the my files:
Login.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as Actions from 'auth/store/actions/index';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router-dom';
import { withStyles } from '#material-ui/core/styles/index';
import { TextFieldFormsy } from '#fuse';
import Formsy from 'formsy-react';
import {
Button, Card, CardContent, Typography,
} from '#material-ui/core';
const styles = () => ({
card: {
width: '100%',
maxWidth: 400,
},
});
class Login extends Component {
state = {
email: '',
password: '',
};
form = React.createRef();
componentDidMount() {
this.props.resetErrorMessage();
}
componentDidUpdate() {
}
onSubmit = (model) => {
this.props.submitMerritosLogin(model);
};
render() {
const { classes } = this.props;
const { email, password } = this.state;
return (
<Card className={`${classes.card} mx-auto md:m-0 merritos-login-card merritos-mobile-register
justify-center items-center flex flex-col`}>
<CardContent className="flex flex-col p-44">
<img className="w-256 mb-32 ml-6 merritos-desktop-display merritos-mobile-image merritos-mobile-images self-center" src="assets/images/logos/merritos-blue-small.png" alt="logo" />
<Typography className="mt-16 font-bold text-24 merritos-login-subtitile merritos-theme-colour">Sign in</Typography>
<Typography className="mb-56 text-grey-darker text-16 font-bold ">to your professional world</Typography>
<Formsy
onValidSubmit={this.onSubmit}
ref={form => this.form = form}
className="flex flex-col justify-center w-full merritos-form"
>
<TextFieldFormsy
className="mb-16 merritos-border-color merritos-accountinfo-textinput"
type="email"
name="email"
label="Email"
value={email}
required
/>
<TextFieldFormsy
className="mb-16 merritos-border-color merritos-accountinfo-textinput"
type="password"
name="password"
label="Password"
value={password}
required
/>
<Button
color="primary"
size="small"
type="submit"
className="text-16 normal-case merritos-login-btn accountinfo-margin"
aria-label="LOG IN"
>
Sign in
</Button>
</Formsy>
</CardContent>
</Card>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
submitMerritosLogin: Actions.submitMerritosLogin,
resetErrorMessage: Actions.resetErrorMessage,
}, dispatch);
}
function mapStateToProps({ auth }) {
return {
user: auth.user,
};
}
export default withStyles(styles, { withTheme: true })(withRouter(
connect(mapStateToProps, mapDispatchToProps)(Login),
));
login.test.js
import React from 'react';
import {render, fireEvent, screen} from '#testing-library/react';
import { shallow } from 'enzyme';
import Login from './Login';
describe('login', () => {
test('renders without crashing', () => {
const wrapper = shallow(<Login.WrappedComponent />);
expect(wrapper.find('Button').text()).toEqual('Sign in');
});
});
While running test I am getting error like bellow:
login › renders without crashing
TypeError: Cannot read property 'card' of undefined
I removed the classes.card and tried then also I got error below:
login › renders without crashing
TypeError: Cannot read property 'resetErrorMessage' of undefined
I am wanting the wrapper component to behave the same way as a wrapper without the withStyles() component.
Looks like you are testing the wrapped component without passing any props while the component requires some props having to be passed.
I think it would work as you pass some needed props to it, it would work as following:
Export Login class in your Login.js
export class Login {
// ...
}
Then import to the test:
// you should import `Login` class to test separately
import { Login } from "./Login";
test('renders without crashing', () => {
const props = {
classes: {
card: 'yourCardClass',
},
resetErrorMessage: jest.fn(),
submitMerritosLogin: jest.fn(),
};
const wrapper = shallow(<Login {...props} />);
// should get called as component did mount
expect(props.resetErrorMessage).toHaveBeenCalled();
expect(wrapper.find('Button').text()).toEqual('Sign in');
});
I'm building a practice app that uses Unsplash to render users photos. I'm using React and Redux. With react-router-dom, I'm trying to follow the docs but I find it very confusing to set up. Here's what I have so far. When I click on a result out of a returned list of results from a search, I want it to render a user page profile.
index.js (make sure I have react-router-do set up correctly):
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
// import store from './app/store';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducers from "./app/reducers/rootReducer";
import * as serviceWorker from './serviceWorker';
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, storeEnhancers(applyMiddleware(thunk)));
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
Top component App
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Router>
<Route link="/userProfile">
<UserProfile />
</Route>
</Router>
</>
);
}
export default App;
search (parent component to searchResults where exists):
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { queryAction } from "../actions/queryAction";
import SearchResults from "./SearchResults";
const Search = (props) => {
const [query, setQuery] = useState("");
console.log(props.searches);
const searchPhotos = async (e) => {
e.preventDefault();
console.log("submitting form");
props.queryAction(query);
};
const showUsers = (user, e) => {
e.preventDefault()
console.log(user)
};
return (
<>
<form className="form" onSubmit={searchPhotos}>
<label className="label" htmlFor="query">
{" "}
</label>
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit" className="button">
Search
</button>
</form>
<SearchResults results={props.searches} showUsers={showUsers} />
</>
);
};
const mapStateToProps = (state) => {
return {
searches: state.searches,
};
};
const mapDispatchToProps = (dispatch) => {
return {
queryAction: (entry) => dispatch(queryAction(entry)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
searchResults:
import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { getUserAction } from "../actions/getUserAction";
import { connect } from "react-redux";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Router>
<Link to="/userProfile" onClick={(e) => handleClick(result, e)}>
{result.username}
</Link>
</Router>
</div>
);
})}
</>
);
};
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(null, mapDispatchToProps)(SearchResults);
and finally the UserProfile component:
import React from 'react';
import { connect } from 'react-redux';
const UserProfile = props => {
console.log(props)
return (
<div>
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
export default connect(mapStateToProps, null)(UserProfile);
app component
import React from "react";
import { Switch, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Switch>
<Route path="/userProfile/:username">
<UserProfile />
</Route>
</Switch>
</>
);
}
export default App;
SearchResults component
import React from "react";
import { Link } from "react-router-dom";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Link to={`/userProfile/${result.username}`}>
{result.username}
</Link>
</div>
);
})}
</>
);
};
export default SearchResults;
UserProfile component
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { getUserAction } from "../actions/getUserAction";
const UserProfile = props => {
useEffect(() => {
props.getUserAction(props.match.params.username)
},[])
console.log(props)
return (
<div>
{props.user
? <div>{user.username}</div>
: <div>Loading...</div>
}
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
Edit: Add a param to your link and remove the onclick. Update the Route to expect a :username param. You can access the param through props in UserProfile component.
Make sure to perform the action or access state when mounting the UserProfile component so you have some data when it renders.
Edit 2: Added UserProfile component to answer. You want to dispatch your action when the component is mounting. Also, set a ternary to show "Loading..." if state.user isn't done being fetched.
I am trying to populate my redux store state with a list of genres from an API, I can get the action to dispatch to the reducer, but the reducer does not seem to update the state because my console.log in src/components/MovieForm.tsx returns the default state of "null" instead of the array of genres and I do not know why, I am trying to see if the state is updated in src/components/MovieForm.tsx in the setInterval where I am logging the state, maybe the problem is how I am accessing the state? Here are the files:
src/actions/movieActions.ts:
import { ActionCreator, Dispatch } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { IMovieState } from '../reducers/movieReducer';
import axios from 'axios';
export enum MovieActionTypes {
ANY = 'ANY',
GENRE = 'GENRE',
}
export interface IMovieGenreAction {
type: MovieActionTypes.GENRE;
genres: any;
}
export type MovieActions = IMovieGenreAction;
/*<Promise<Return Type>, State Interface, Type of Param, Type of Action> */
export const movieAction: ActionCreator<ThunkAction<Promise<any>, IMovieState, void, IMovieGenreAction>> = () => {
return async (dispatch: Dispatch) => {
try {
console.log('movieActions called')
let res = await axios.get(`https://api.themoviedb.org/3/genre/movie/list?api_key=${process.env.REACT_APP_MOVIE_API_KEY}&language=en-US`)
console.log(res.data.genres)
dispatch({
genres: res.data.genres,
type: MovieActionTypes.GENRE
})
} catch (err) {
console.log(err);
}
}
};
src/reducers/movieReducer.ts:
import { Reducer } from 'redux';
import { MovieActionTypes, MovieActions } from '../actions/movieActions';
export interface IMovieState {
//property: any;
genres: any;
}
const initialMovieState: IMovieState = {
//property: null,
genres: null,
};
export const movieReducer: Reducer<IMovieState, MovieActions> = (
state = initialMovieState,
action
) => {
switch (action.type) {
case MovieActionTypes.GENRE: {
console.log('MovieActionTypes.GENRE called')
return {
genres: action.genres,
};
}
default:
console.log('Default action called')
return state;
}
};
src/store/store.ts:
import { applyMiddleware, combineReducers, createStore, Store } from 'redux';
import thunk from 'redux-thunk';
import { IMovieState, movieReducer } from '../reducers/movieReducer';
// Create an interface for the application state
export interface IAppState {
movieState: IMovieState
}
// Create the root reducer
const rootReducer = combineReducers<IAppState>({
movieState: movieReducer
});
// Create a configure store function of type `IAppState`
export default function configureStore(): Store<IAppState, any> {
const store = createStore(rootReducer, undefined, applyMiddleware(thunk));
return store;
}
src/components/MovieForm.tsx (the file that is supposed to dispatch the action):
import React, { useState } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Box from '#material-ui/core/Box';
import Select from '#material-ui/core/Select';
import MenuItem from '#material-ui/core/MenuItem';
import { spacing } from '#material-ui/system';
import Card from '#material-ui/core/Card';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import { CardHeader, TextField, CircularProgress } from '#material-ui/core';
import { useDispatch, useSelector } from 'react-redux';
import { movieAction } from '../actions/movieActions';
import { IAppState } from '../store/store';
import axios from 'axios';
const MovieForm = () => {
const dispatch = useDispatch()
const getGenres = () => {
console.log('actions dispatched')
dispatch(movieAction())
}
const genres = useSelector((state: IAppState) => state.movieState.genres);
//const [genreChoice, setGenreChoice] = useState('')
return (
<>
<h1>Movie Suggester</h1>
<Paper elevation={3}>
<Box p={10}>
<Card>
<div>Hello World. </div>
<Select onChange={() => console.log(genres)}>
<MenuItem>
hello
</MenuItem>
<br />
<br />
</Select>
<Button onClick={() => {
getGenres()
setTimeout(function(){
console.log(genres)
}, 5000)
}}>genres list</Button>
<Button onClick={() => console.log(axios.get(`https://api.themoviedb.org/3/discover/movie?api_key=${process.env.REACT_APP_MOVIE_API_KEY}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&with_genres=35&page=1`))}>Click me</Button>
</Card>
</Box>
</Paper>
</>
)
}
export default MovieForm
and here is the src/index.tsx in case the problem is here and I'm unaware:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom';
import './index.css';
import CssBaseline from '#material-ui/core/CssBaseline';
import { ThemeProvider } from '#material-ui/core/styles';
import theme from './theme';
import App from './App';
import configureStore from './store/store';
const store = configureStore();
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Router>
<App />
</Router>
</ThemeProvider>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
Thanks for taking a look at this and and attempting to help me see what I am unable to!
In my file called app.jsx i have one import from the component "atividades":
import React from 'react'
import { Container } from 'semantic-ui-react'
import atividades from '../atividades/atividades'
export default props => (
<Container>
<h1>Teste</h1>
<atividades />
</Container>
)
But only the h1 is rendering...
This is my atividades component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { Form } from 'semantic-ui-react'
import { addWorkout, searchWorkout } from './workOutActions'
import { Button, Icon } from 'semantic-ui-react'
const workoutOptions = [
{ text: 'correr', value: 'run' },
{ text: 'nadar', value: 'swimming' },
{ text: 'andar de bicicleta', value: 'bike' },
]
class atividades extends Component {
constructor(props){
super(props)
}
componentWillMount(){
this.props.searchWorkout()
}
render() {
const { addWorkout, searchWorkout, tempoGasto, tipoTarefa, data} = this.props
return (
<div role='form'>
<h6>Inserir atividade</h6>
<Form>
<Form.Group widths='equal'>
<Form.Input fluid placeholder='Tempo gasto' />
<Form.Select
fluid
label='Atividade'
options={workoutOptions}
/>
<Button animated='vertical'>
<Button.Content hidden>Shop</Button.Content>
<Button.Content visible>
<Icon name='shop' />
</Button.Content>
</Button>
</Form.Group>
</Form>
</div>
)
}
}
const mapStateToProps = state => ({tempoGasto: state.workout.tempoGasto, tipoTarefa: state.workout.tipoTarefa, data: state.workout.data})
const mapDispatchToProps = dispatch =>
bindActionCreators({ addWorkout, searchWorkout }, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(atividades)
It's not showing nothing in the console, but the element workout is not rendering, the visual studio code says that the import is not being used.
The first letter of React components must be capitalized or it thinks it’s a built in component like div or p or span.
This link has more info:
ReactJS component names must begin with capital letters?
I am new in React. As I read many documents, I realized that the state of the application should managed outside each components. And I choose Redux for my project.And I tried to pass username and password from my SigIn component. But when I click on the login button , the default statement inside the switch is always executed.The code are given below.
SignIn.jsis as below
import React from 'react';
import Header from './Head.js';
import { logIn } from '../actions/index.js';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
class SignIn extends React.Component{
constructor(){
super();
this.logInClick = this.logInClick.bind(this);
}
logInClick() {
let { dispatch } = this.props;
const data = {username:'sojimon#gmail.com', password:'12345'}
// this.props.logIn(data);
this.props.dispatch(logIn(data));
}
render(){
return(
<div>
<Header/>
<br/>
<div className="col-md-4 col-md-offset-4">
<div className="well">
<h4 className="signin_header">Sign In</h4>
<div>
<div>
<label>Email:</label>
<input type="text" className="form-control" />
</div>
<div>
<label>Password:</label>
<input type="text" className="form-control"/>
</div>
<br/>
<button className="btn btn-primary" onClick={ this.logInClick }>Login</button>
</div>
</div>
</div>
</div>
)
}
}
const matchDispatchToProps = (dispatch) => ({
// logIn: (data) => dispatch(logIn(data)),
})
SignIn.propTypes = {
logIn: React.PropTypes.func
}
export default connect (matchDispatchToProps)(SignIn);
And my action/index.js as follows,
import * as types from './types.js';
export const logIn = (state, data) => {
return {
type: types.LOG_IN,
state
}
}
And reducers/logIn.js is,
import React from 'react';
import { LOG_IN } from '../actions/types.js'
const logIn = (state = [], action) => {
switch (action.type) {
case 'LOG_IN':
console.log('switch Case ==> LOG_IN');
return [
...state,
{
username: 'asdf',
password: '123',
}
]
// return action.logIn
default:
console.log('switch Case ==> Default');
return state
}
}
export default logIn
And created store in index.js file as,
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/App';
import './app/index.css';
import Routes from './app/route';
import { createStore } from 'redux' // import store
import { Provider } from 'react-redux' // import provider
import myApp from './app/reducers/index.js'
let store = createStore(myApp);
ReactDOM.render(
<Provider store={store}>
<Routes />
</Provider>,
document.getElementById('root')
);
export default store;