I got the following error:
/src/Components/Home/post.jsx
Module not found: Can't resolve 'material-ui/FlatButton' in /Users/apple/Documents/dev/source/sm-ui/servicemonster-ui
I tried to install material-ui but it failed. How can I fix this?
post.jsx:
import React from 'react';
import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card';
import FlatButton from 'material-ui/FlatButton';
import Toggle from 'material-ui/Toggle';
export default class CardExampleControlled extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: false,
};
}
handleExpandChange = (expanded) => {
this.setState({expanded: expanded});
};
handleToggle = (event, toggle) => {
this.setState({expanded: toggle});
};
handleExpand = () => {
this.setState({expanded: true});
};
handleReduce = () => {
this.setState({expanded: false});
};
render() {
return (
<Card expanded={this.state.expanded} onExpandChange={this.handleExpandChange}>
<CardHeader
title="URL Avatar"
subtitle="Subtitle"
avatar="images/ok-128.jpg"
actAsExpander={true}
showExpandableButton={true}
/>
<CardText>
<Toggle
toggled={this.state.expanded}
onToggle={this.handleToggle}
labelPosition="right"
label="This toggle controls the expanded state of the component."
/>
</CardText>
<CardMedia
expandable={true}
overlay={<CardTitle title="Overlay title" subtitle="Overlay subtitle" />}
>
<img src="images/nature-600-337.jpg" alt="" />
</CardMedia>
<CardTitle title="Card title" subtitle="Card subtitle" expandable={true} />
<CardText expandable={true}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Donec mattis pretium massa. Aliquam erat volutpat. Nulla facilisi.
Donec vulputate interdum sollicitudin. Nunc lacinia auctor quam sed pellentesque.
Aliquam dui mauris, mattis quis lacus id, pellentesque lobortis odio.
</CardText>
<CardActions>
<FlatButton label="Expand" onClick={this.handleExpand} />
<FlatButton label="Reduce" onClick={this.handleReduce} />
</CardActions>
</Card>
);
}
}
//error:./src/Components/Home/post.jsx Module not found: Can't resolve
'material-ui/FlatButton' in
'/Users/apple/Documents/dev/source/sm-ui/servicemonster-ui/src/Components/Home'
home.jsx:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import EditLocation from 'material-ui-icons/EditLocation';
import {Input, IconButton, } from 'material-ui';
import { Services, Search, } from './';
import {CardExampleControlled} from './post.jsx'
const Icon = require('../../assets/icon.svg');
class home extends Component {
render() {
// const { services, fetched, fetching, error } = this.props;
return (
<div className="home-page">
<div className="home">
<img className="logo" src={Icon} alt="Service Monster" />
<Search />
<div className="flex flex-row flex-center">
<IconButton className="" color="contrast" aria-label="Menu">
<EditLocation />
</IconButton>
<Input
defaultValue="india"
className="location"
inputProps={{
'aria-label': 'location',
}}
/>
</div>
</div>
<Services />
</div>
);
}
}
const mapStateToProps = (store) => {
return {
services: store.services.data,
fetched: store.services.fetched,
fetching: store.services.fetching,
error: store.services.error
}
}
const Home = connect(mapStateToProps)(home);
export { Home };
index.js:
import { Home, } from './Home.jsx';
import { Services, } from './Services.jsx';
import { Menus, } from './Menu.jsx';
import { Search } from './Search';
import { CardExampleControlled } from './post.jsx';
export { Home, Menus, Services, Search, CardExampleControlled };
There is version mismatch of material-ui.
From your code FlatButton component belongs to version v0.20 and probably you have installed v1.0.0-beta.xx which does not have that component.
so you need to install npm install material-ui for currently stable release. instead of npm install material-ui#next
I had the same problem when trying out material-ui.
It felt kind of tricky because it is not explicitly documented and there are differences between v0.20 and v1.0. There is no Button component in v0.20, for example look at http://www.material-ui.com/#/components/raised-button
But I have found solution on https://github.com/mui-org/material-ui/tree/master ( for version v0.20.0), titled Usage
you will need to wrap your 'App' (root react component) with tag <MuiThemeProvider>
I have created test app with react-create-app script, and changed file App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class App extends Component {
render() {
return (
<MuiThemeProvider>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<RaisedButton label="Primary" primary={true} />
</div>
</MuiThemeProvider>
);
}
}
export default App;
voila, now it works :-)
If you've installed the non-alpha version of Material-UI you should be able to resolve this warning by changing your import line from:
import FlatButton from 'material-ui/FlatButton';
to
import { FlatButton } from 'material-ui';
Related
Using Gatsbyjs and GrapghQL queries don't work on the site. They work in the testing environment.
import React from "react";
import { Link, graphql, useStaticQuery } from "gatsby";
import Img from "gatsby-image";
import PropTypes from "prop-types";
// Components
import Layout from "../components/layout";
// CSS
import "../css/index.css";
// Page
const Index = () => {
return (
<Layout>
{/* Hero */}
<section id="hero">
<div className="hero-title container">
<h1>Web design and development</h1>
<h2>Costume Solutions for companies and individuals</h2>
</div>
</section>
{/* Services */}
<section id="services">
<div className="title">
<h2>Our Services</h2>
<h3>We Provide You Quality</h3>
</div>
<div className="services container">
<div className="service web-design">
<h2>Web Design</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
<div className="service social-media">
<h2>Social Media</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
<div className="service video-production">
<h2>Video Production</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
</div>
</div>
</section>
{/* portfolio */}
<section id="Portfolio">
<div className="title">
<h2>Portfolio</h2>
<h3>Some Of Our Awesome Projects</h3>
</div>
<div className="portfolio container"></div>
</section>
</Layout>
);
};
// Index.propTypes = {
// data: PropTypes.object.isrequired,
// };
export const query = graphql`
{
protfolioImages: allFile(
filter: { relativeDirectory: { eq: "portfolio" } }
) {
nodes {
id
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
`;
export default Index;
ERROR:
Multiple "root" queries found in file: "cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410" and "cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410".
Only the first ("cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410") will be registered.
Instead of:
1 | query cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410 {
2 | bar {
3 | #...
4 | }
5 | }
6 |
7 | query cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410 {
8 | foo {
9 | #...
10 | }
11 | }
Do:
1 | query cUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410AndCUsersAndreDocumentsProgramingjarboeStudiossrcpagesindexJsx2863101410 {
2 | bar {
3 | #...
4 | }
5 | foo {
6 | #...
7 | }
8 | }
Looking into this it seems like other users have experienced this problem and have come up with two possible causes and solutions.
It may be related to your CMD. Apparently running Gatsby Build from Windows terminal produces the 'Multiple root queries' error, while running the same command with git bash compiles successfully.
Or Having multiple graphql queries on the same page, in this case separating the queries into their own files and importing them back again fixes the problem.
Have been searching for a while. I am using Gatsby and I have no errors in the console or terminal but values are undefined. Could it be because of the second "data" under "articles"?
GraphiQL Query:
Query/Component Code:
import React, { Component } from 'react'
import { graphql } from 'gatsby'
// This query is executed at build time by Gatsby.
export const GatsbyQuery = graphql`
{
umdHub {
articles {
data {
title
}
}
}
}
`
class ClientFetchingExample extends Component {
render() {
const {
umdHub: { articles },
} = this.props.data
console.log(articles.title)
return (
<div style={{ textAlign: 'center', width: '600px', margin: '50px auto' }}>
<h1>{articles.title} - Is the title of the article</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>
)
}
}
export default ClientFetchingExample
You already halfway there just small changes
const {
umdHub: { articles },
} = this.props.data
Now you have this articles object like this
articles: {
data: [
{
title: 'Learning to Listen'
},
{
title: 'Second Title'
}
...
]
}
Now you need to loop over data
import _ from 'lodash'
const data = _.get(articles, 'data', [])
_.map(data, title => {
console.log({ title })
})
If you want to only first title then
const title = _.get(articles, 'data[0].title', 'default value or blank string')
Here lodash is A modern JavaScript utility library delivering modularity, performance & extras.
I am trying to implement search/filter in my react project but can't seem to get it right.
Screenshot
This is what the user should be able to do:
Filter by text
Filter by location using the checkbox
or filter by job category
What I have right now is that I can filter by both text and checkbox, but I want the user to be able to search by text, e.g., "Dallas", "information technology", "sales" and then filter further by category or by location using the returned data from the text search.
This is my set up:
Searching by text uses a reducer to filter the array by search text
Checking a location or category checkbox stores the value in the category or keyword array
In the combine reducer file, I filter the jobs array in the store and memorize it using reselect.
I have a couple of glitches with the search text and checkbox functionalities. The filter by location checkbox functionality works fine by itself; however, when a checkbox is selected, and I enter search text, checkbox gets deselected. I want the checkbox to be still checked and displayed in the window when text is entered in the input field.
I'm a newbie, any help, suggestions, or a better approach to this functionality will be greatly appreciated.
SEARCHFORM.JS:
import React, {Component} from 'react';
//import {FormGroup, FormControl, InputGroup} from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { searchJobs, getJobs } from '../actions/jobaction';
import {selectKeywordCity,deselectKeywordCity} from '../actions/selectDeselectCityAction';
import {selectKeywordCategory,deselectKeywordCategory} from '../actions/selectDeselectCategoryAction';
class Search extends Component{
constructor(props){
super();
this.state = {
keyword:'',
checked:[]
};
this.handleChk = this.handleChk.bind(this);
}
handleInput = (event) => {
this.setState({
keyword: event.target.value
});
this.props.searchJobs(this.state.keyword);
}
/* handleSearch = (event) => {
event.preventDefault();
this.props.getJobs(this.state.keyword);
} */
clearForm = () => {
this.inputSearch.value = "";
this.setState({
keyword: this.inputSearch.value
});
this.props.getJobs();
}
handleChk = (e, keyword, type) => {
if(type==='city'){
e.target.checked ? this.props.selectKeywordCity(keyword) : this.props.deselectKeywordCity(keyword);
}else if(type==='category'){
e.target.checked ? this.props.selectKeywordCategory(keyword) : this.props.deselectKeywordCategory(keyword);
}
}
//Render Cities
renderCities(data){
if(data){
const cities = Object.keys(data).map(el => {
return data[el].city
}).sort()
.reduce((city,name) => {
const cityCount = city[name] ? city[name] + 1 : 1;
return{
...city, [name]:cityCount,
};
},{});
return Object.keys(cities).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'city')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{cities[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
//Render Job Categories
renderCategories(data){
if(data){
const categories = Object.keys(data).map(el => {
return data[el].category
}).sort()
.reduce((category,name) => {
const categoryCount = category[name] ? category[name] + 1 : 1;
return{
...category, [name]:categoryCount,
};
},{});
return Object.keys(categories).map((keyname, index) => {
return (
<li key={keyname}>
<input type="checkbox"
onChange={e=>this.handleChk(e,keyname,'category')}
className="chkbox"
/>
{keyname}
<span className="pull-right">{categories[keyname]}</span>
</li>)
})
}else{
return("nada")
}
}
render(){
let closeBtn = null;
if(this.state.keyword){
closeBtn = (<i className="fa fa-times" onClick={this.clearForm}></i>);
}
return(
<div>
<div className="side-search">
<i className="fa fa-search"></i>
<input type="text"
placeholder="Keyword Search"
ref={el => this.inputSearch = el}
onChange={this.handleInput}
value={this.state.keyword}
/>
{closeBtn}
</div>
<div className="chk_boxes">
<h4>Location</h4>
<ul>
{
this.renderCities(this.props.jobs)
}
</ul>
</div>
<div className="chk_boxes">
<h4>Category</h4>
<ul>
{
this.renderCategories(this.props.jobs)
}
</ul>
</div>
</div>
)
}
}
function mapStateToProps(state){
return{
jobs: state.jobs.jobs
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({searchJobs,
getJobs,
selectKeywordCity,
deselectKeywordCity,
selectKeywordCategory,
deselectKeywordCategory},
dispatch)
}
export default connect(mapStateToProps,mapDispatchToProps)(Search);
JOBACTION:
import axios from 'axios';
const FETCH_URL = 'http://localhost:3006';
/* GET ALL JOB LISTINGS */
export function getJobs(){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs`)
.then(function(response){
dispatch({type:'GET_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'GET_JOBS_ERROR', payload:err});
})
}
}
/* GET ALL JOB LISTINGS */
export function searchJobs(keyword){
return function(dispatch){
axios.get(`${FETCH_URL}/jobs?q=${keyword}`)
.then(function(response){
dispatch({type:'SEARCH_JOBS',payload:response.data})
}).catch(function(err){
dispatch({type:'SEARCH_JOBS_ERROR', payload:err});
})
}
}
JOBSLIST REDUCER:
export function Listings (state={}, action){
switch(action.type){
case 'GET_JOBS':
return{...state, jobs:action.payload}
case 'SEARCH_JOBS':
return{...state, jobs:action.payload}
default:
return state;
}
}
SELECTDESELECT(CHECKBOX) REDUCER:
const INITIAL_STATE = {
keywords:[]
}
export function filterCity(state=INITIAL_STATE, action){
switch(action.type){
case 'SELECT_CITY':
return{
keywords:[...state.keywords, action.payload]
}
case 'DESELECT_CITY':
const currentKeywordToDelete = [...state.keywords];
const indexOfKeyword = currentKeywordToDelete.findIndex(function(keyword){
return keyword === action.payload
});
return{
keywords:[...state.keywords.slice(0,indexOfKeyword), ...currentKeywordToDelete.slice(indexOfKeyword+1) ]
}
default:
return state;
}
}
COMBINEREDUCER:
import {combineReducers} from 'redux';
import {createSelector} from 'reselect';
import {Listings} from './listingsReducer';
import {filterCity} from './select-deselect-city';
import {filterCategory} from './select-deselect-category';
const reducer = combineReducers({
jobs: Listings,
keywords: filterCity,
category: filterCategory
});
export const selectJobList = (state) => state.jobs.jobs;
export const selectCities = (state) => state.keywords.keywords;
//export const selectCategories = (state) => state.categories.categories;
export const selectFilteredJobs = createSelector(
selectJobList,selectCities,
(jobs,cities) => {
if(cities && cities.length > 0){
const filteredData = Object.keys(jobs)
.filter(key => cities.includes(jobs[key].city))
.reduce((obj, key) => {
return [...obj, {...jobs[key]}]
}, []);
//console.log(filteredData);
return filteredData;
}else{
return jobs
}
}
)
export default reducer;
JSON DATA:
{
"jobs": [
{
"id": 1,
"title": "Financial Analyst II",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. .",
"category": "Sales",
"dept": "Finance",
"date": "07/11/2018",
"experience": ["Bachelors Degree","Five years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "10,000"
},
{
"id": 2,
"title": "Application Support Speacialist",
"description": "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.",
"category": "Information Technology",
"dept": "Accounting",
"date": "07/10/2018",
"experience": ["Bachelors Degree","Two years of sales experience"],
"city": "Dallas",
"state":"Texas",
"salary": "20,000"
}
}
I'm trying to get a list of news and then render them in the template of my component.
I was learning from this tutorial, which is a little bit out of date, http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/
The news item class looks like this
export class NewsItemComponent {
text: string;
date: Date;
author: string;
link: string
constructor(text: string,
date: Date,
author: string,
link: string) {
this.text = text;
this.date = date;
this.author = author;
this.link = link;
}
}
The service retrieving the data:
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { NewsItemComponent } from './news-item/news-item.component';
#Injectable()
export class NewsService {
constructor(public http: Http) {
console.log('Creating NewsService');
}
getNews() {
let url = 'http://localhost/test-api/';
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this.http.post(url, '', options)
// initial transform - result to json
.subscribe( (res:Response) => {
let articles: Array<any> = [];
res.json().map((articles: Array<any>) => {
articles.push(new NewsItemComponent('asd', new Date(), 'asdf', 'asdf'));
});
console.log('articles', articles);
return articles
});
}
}
The main component:
import { Component, OnInit, AfterViewInit, AfterViewChecked } from '#angular/core';
import { Http, Response, RequestOptions, Headers, Request, RequestMethod } from '#angular/http';
import { TranslateService } from 'ng2-translate/ng2-translate';
import { NewsItemComponent } from './news-item/news-item.component';
import { NewsService } from './news-service.service';
#Component({
selector: 'news-section',
templateUrl: './news-section.component.html',
styleUrls: ['./news-section.component.scss'],
providers: [ NewsService ]
})
export class NewsSectionComponent implements AfterViewInit {
articles: NewsItemComponent[];
constructor(translate: TranslateService, private http: Http, public newsService: NewsService) {
translate.setDefaultLang('en');
translate.use('en');
this.articles = [];
newsService.getNews().subscribe(res => this.articles = res);
}
}
And the view:
<section id="news" sectionVisible>
<div>
<h1>{{ 'Nav.News' | translate }}</h1>
</div>
<div class="news-wrapper clear-fix">
<div class="column carousel-cell" ngFor="#article of articles">
<article>
<a href="#" class="hovered">
<div class="bg-hover">
<p>
Visit
</p>
</div>
</a>
<h2>News title</h2>
<time>21.10.2015 16:30</time>
<p>
Etiam faucibus sodales leo, rutrum molestie nisi luctus in. Duis quis viverra diam, vitae hendrerit augue. Donec ultricies nisi vel placerat sodales. Donec varius convallis mauris egestas vulputate. Integer fermentum tincidunt hendrerit. Donec eget nisl
eros. Pellentesque a fringilla lorem.
</p>
</article>
</div>
</div>
</section>
Of course in the view the lorem ipsum parts will be changed by the data retrieved. But there is something wrong with the service:
newsService.getNews().subscribe(res => this.articles = res);
this line raises an error:
Property subscribe does not exist on type void
And I have no idea why it is caused.
Any hints?
You need to remove the subscribe method from your service. Services should only return the Observables, subscribing should be done in the actual component.
Subscribing to an Observable is equal to calling a function. You want to retrieve the result in your component, so that's where you need to subscribe. The service should only hold the logic to prepare the data after the HTTP call (calling for example res.json()).
Service
getNews() {
let url = 'http://localhost/test-api/';
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.post(url, '', options)
.map(res => res.json());
}
Component:
newsService.getNews()
.subscribe( news => {
let articles: Array<any> = [];
news.map((articles: Array<any>) => {
articles.push(new NewsItemComponent('asd', new Date(), 'asdf', 'asdf'));
});
this.articles = articles
});
I have a problem I can't solve trying to use redux-form. I'm trying the Erikras boilerplate. I want the form to be a component and the parent to call handleSubmit (for the moment with a console.log just to confirm it works). Here, the two:
import React, {Component, PropTypes} from 'react';
import Helmet from 'react-helmet';
import {initialize} from 'redux-form';
import {connect} from 'react-redux';
import * as membersActions from 'redux/modules/members';
import {isLoaded, loadMembers} from 'redux/modules/members';
import { DashboardList } from 'components';
import { DashboardHeader } from 'components';
import { DashboardAdding } from 'components';
import { asyncConnect } from 'redux-async-connect';
#asyncConnect([{
deferred: true,
promise: ({store: {dispatch, getState}}) => {
if (!isLoaded(getState())) {
return dispatch(loadMembers());
}
}
}])
#connect(
state => ({
members: state.members.data,
error: state.members.error,
loading: state.members.loading
}),
{...membersActions, initialize })
export default class Dashboard extends Component {
static propTypes = {
initialize: PropTypes.func.isRequired,
members: PropTypes.array,
loadMembers: PropTypes.func.isRequired
}
handleSubmit = (data) => {
console.log(data);
this.props.initialize('dashAdding', {});
}
handleInitialize = () => {
this.props.initialize('dashAdding', {
pseudo: 'Pibo',
email: 'pibirino#gmail.com'
});
}
render() {
const {members} = this.props;
return (
<div className="container">
<h1>Dashboard</h1>
<Helmet title="Dashboard"/>
<DashboardHeader />
<div>
<DashboardList members={members}/>
<h3>Ici commence le form</h3>
<div style={{textAlign: 'center', margin: 15}}>
<button className="btn btn-primary" onClick={this.handleInitialize}>
<i className="fa fa-pencil"/> Initialize Form
</button>
</div>
</div>
<DashboardAdding onSubmit={this.handleSubmit}/>
<p>Bleeeeah!!</p>
</div>
);
}
}
and here the child:
import React, {Component, PropTypes} from 'react';
import {reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import memberValidation from './memberValidation';
#reduxForm({
form: 'dashAdding',
fields: ['pseudo', 'email'],
validate: memberValidation
})
export default class DashboardAdding extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired
}
render() {
const {
fields: { pseudo, email},
handleSubmit,
resetForm
} = this.props;
const renderInput = (field, label) =>
<div className={'form-group' + (field.error && field.touched ? ' has-error' : '')}>
<label htmlFor={field.name} className="col-sm-2">{label}</label>
<div className={'col-sm-8 '}>
<input type="text" className="form-control" id={field.name} {...field}/>
</div>
</div>;
return (
<div>
<form className="form-horizontal" onSubmit={handleSubmit}>
{renderInput(pseudo, 'Full Name')}
{renderInput(email, 'Email', true)}
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-success" onClick={handleSubmit}>
<i className="fa fa-paper-plane"/> Submit
</button>
<button className="btn btn-warning" onClick={resetForm} style={{marginLeft: 15}}>
<i className="fa fa-undo"/> Reset
</button>
</div>
</div>
</form>
</div>
);
}
}
So... it doesn't work I think I'm missing some important knowledge. I thought that the reason is because the form component is dumb, and it doesn't have the dispatch function. So, I tryed to add this (several times in several different ways) importing the action creator from the specific folder:
#connect(() => ({}),
dispatch => actionCreators( dispatch)
)
But I don't still get what I want. What's the problem?
So, finally I found the answer by myself. In fact, I decided to not use #connect, whitch is deprecated (despite it's still used in the boilerplate) and to use connect, as in the example of the redux-form documentation. The only change concerned the parent, but I'll post them both in case I missed something. The following code works good.
Here is the code:
import React, {Component, PropTypes} from 'react';
import Helmet from 'react-helmet';
import {initialize} from 'redux-form';
import {connect} from 'react-redux';
import * as membersActions from 'redux/modules/members';
import {isLoaded, loadMembers} from 'redux/modules/members';
import { DashboardList } from 'components';
import { DashboardHeader } from 'components';
import { DashboardAdding } from 'components';
import { asyncConnect } from 'redux-async-connect';
#asyncConnect([{
deferred: true,
promise: ({store: {dispatch, getState}}) => {
if (!isLoaded(getState())) {
return dispatch(loadMembers());
}
}
}])
class Dashboard extends Component {
static propTypes = {
members: PropTypes.array,
error: PropTypes.string,
loading: PropTypes.bool,
addMember: PropTypes.func,
initialize: PropTypes.func.isRequired,
newMemberData: PropTypes.object
}
handleSubmit = (data, dispatch) => {
dispatch(addMember(JSON.stringify(data)));
this.props.initialize('dashboardForm', {});
}
handleInitialize = () => {
this.props.initialize('dashboardForm', {
pseudo: 'Pibo',
email: 'pibirino#gmail.com'
});
}
render() {
const {members} = this.props;
return (
<div className="container">
<h1>Dashboard</h1>
<Helmet title="Dashboard"/>
<DashboardHeader />
<div>
<DashboardList members={members}/>
<h3>Ici commence le form</h3>
<div style={{textAlign: 'center', margin: 15}}>
<button className="btn btn-primary" onClick={this.handleInitialize}>
<i className="fa fa-pencil"/> Initialize Form
</button>
</div>
</div>
<DashboardAdding onSubmit={this.handleSubmit}/>
</div>
);
}
}
function mapStateToProps(state) {
return {
members: state.members.data,
error: state.members.error,
loading: state.members.loading,
newMemberData: state.addSingleMember.data
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators({
addActions,
initialize: initialize
}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Dashboard);
...and the child component:
import React, {Component, PropTypes} from 'react';
import {reduxForm} from 'redux-form';
import memberValidation from './memberValidation';
class DashboardAdding extends Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired
}
render() {
const {
fields: { pseudo, email},
handleSubmit,
resetForm
} = this.props;
const renderInput = (field, label) =>
<div className={'form-group' + (field.error && field.touched ? ' has-error' : '')}>
<label htmlFor={field.name} className="col-sm-2">{label}</label>
<div className={'col-sm-8 '}>
<input type="text" className="form-control" id={field.name} {...field}/>
{field.error && field.touched && <div className="text-danger">{field.error}</div>}
</div>
</div>;
return (
<div>
<form className="form-horizontal" onSubmit={handleSubmit.bind(this)}>
{renderInput(pseudo, 'Pseudo')}
{renderInput(email, 'Email', true)}
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-success" onClick={handleSubmit}>
<i className="fa fa-paper-plane"/> Submit
</button>
<button className="btn btn-warning" onClick={resetForm} style={{marginLeft: 15}}>
<i className="fa fa-undo"/> Reset
</button>
</div>
</div>
</form>
</div>
);
}
}
export default reduxForm({
form: 'dashboardForm',
fields: ['pseudo', 'email'],
validate: memberValidation,
asyncBlurFields: ['email']
})(DashboardAdding);
The addMember function contains obviously a promise.
I hope it will helps somebody :-)