Search functionality in reactjs - javascript

import React, { PropTypes } from 'react';
import { Link, browserHistory } from 'react-router';
import * as DataConnectionAction from '../../actions/dataconnectionAction.jsx';
import DataConnectionStore from '../../store/dataconnectionstore.jsx';
class DataSource extends React.Component {
constructor(props) {
super(props);
this.state = {
datasourcelist: [],
};
this._dataconnectionStoreChange = this._dataconnectionStoreChange.bind(this);
}
componentWillMount() {
DataConnectionStore.on('change', this._dataconnectionStoreChange);
}
componentWillUnmount() {
DataConnectionStore.removeListener('change', this._dataconnectionStoreChange);
}
componentDidMount() {
DataConnectionAction._getDataSourcesList();
}
_dataconnectionStoreChange(type) {
if (type == 'DataSourcesList') {
let datasourcelist = DataConnectionStore._getDataSourceList() || {};
this.setState({ datasourcelist: datasourcelist.dataconnections });
}
}
DataSourceView(el) {
let data = {
id: el.dataConnectionName
}
}
_handleSearchChange(e) {
let value = e.target.value;
let lowercasedValue = value.toLowerCase();
let datasourcedata = this.state.datasourcelist;
let datasourcelist = datasourcedata && datasourcedata.filter(el => el.dataConnectionName.toLowerCase().includes(lowercasedValue));
this.setState({ datasourcelist });
}
DataSourcesCardUI() {
let datasourcedata = this.state.datasourcelist;
return (
datasourcedata && datasourcedata.map((el) =>
<div key={el.key}>
<div className="col-md-3 topadjust">
<div className="panel panel-default datasource_panel ">
<div className="panel-heading">
<h5 className="panel_title"><i className="fa fa-database"></i> {el.dataConnectionName}</h5>
</div>
<Link className="panel-body" onClick={this.DataSourceView.bind(this, el)}>
<div className="datasource_txt text-center">
<h6>{el.databaseHost}</h6>
<h6>{el.dataConnectionType} </h6>
<p>{el.createdDate}</p>
</div>
</Link>
</div>
</div>
</div>
)
);
}
render() {
return (
<div>
<section className="content_block">
<div className="container-fluid">
<div className="row dashboard_list">
{this.DataSourcesCardUI()}
</div>
</div>
</section>
</div>
);
}
}
export default DataSource;
Here I am getting one issue, that is I can able to filter based on the dataConnectionName, but when I am trying to filter with change of name it is filtering from the first filter array data.
But, I need to filter based on data array if i remove and type again.
Example:
when I tried search with Cu I am getting properly. but again when i remove Cu and search for User It is not searching from data array It is searching from filter array data. Instead of that when i remove and search with other key it should get filtered from data array.
Please Guide me what i am doing wrong.

Instead of overwriting the data in your state, you could keep a separate array in which you put all the elements that match the search.
Example
let data = [
{
dataConnectionName: "Customer_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-23",
createBy: "Admin"
},
{
dataConnectionName: "User_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-24",
createBy: "Admin"
},
{
dataConnectionName: "Manager_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-25",
createBy: "Admin"
},
{
dataConnectionName: "Director_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-26",
createBy: "Admin"
}
];
// Give each element a unique id that is used as key
data.forEach(el => el.id = Math.random());
class App extends React.Component {
state = {
data,
filteredData: data
};
_handleSearchChange = e => {
const { value } = e.target;
const lowercasedValue = value.toLowerCase();
this.setState(prevState => {
const filteredData = prevState.data.filter(el =>
el.dataConnectionName.toLowerCase().includes(lowercasedValue)
);
return { filteredData };
});
};
render() {
const { filteredData } = this.state;
return (
<div>
<input onChange={this._handleSearchChange} placeholder="Search"/>
{filteredData.map(el => (
<div key={el.key}>
<div>
{el.dataConnectionName} - {el.pluginName} - {el.createdDate} - {el.createBy}
</div>
</div>
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Related

Show contents of array in render

I am using MERN stack and Redux. I have created an array in the state 'comments' which is updated via the clickHandler function with elements from the global state (accessed via props). When i try to show the contents of the array in the render i just get the length of it. How would i show the properties of the elements for example title.
import React, { Component } from "react";
import PropTypes from "prop-types";
import GoogleSearch from "./GoogleSearch";
import { connect } from "react-redux";
import { fetchSubjects } from "../../actions/subject";
import { fetchComments } from "../../actions/comment";
import store from "../../store";
class Subject extends Component {
// on loading the subjects and comments
// are fetched from the database
componentDidMount() {
this.props.fetchSubjects();
this.props.fetchComments();
}
constructor(props) {
super(props);
this.state = {
// set inital state for subjects description
// and summary to invisible
viewDesription: -1,
viewSummary: -1,
comments: [],
};
}
componentWillReceiveProps(nextProps) {
// new subject and comments are added to the top
if (nextProps.newPost) {
this.props.subjects.unshift(nextProps.newPost);
}
if (nextProps.newPost) {
this.props.comments.unshift(nextProps.newPost);
}
}
clickHandler = (id) => {
// when a subject title is clicked pass in its id
// and make the desciption visible
const { viewDescription } = this.state;
this.setState({ viewDescription: viewDescription === id ? -1 : id });
// clear the existing comments in state
this.setState({
comments: [],
});
// loop through the comment items in the global state
// and add any with the same subjects id passed in to the array
var i;
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
this.setState({
comments: this.state.comments.unshift(this.props.comments[i]),
});
}
} // set local storage to the id for the subject that has been clicked
localStorage.setItem("passedSubject", id);
};
// hovering on and off subjects toggles the visibility of the summary
hoverHandler = (id) => {
this.setState({ viewSummary: id });
};
hoverOffHandler = () => {
this.setState({ viewSummary: -1 });
};
render() {
const subjectItems = this.props.subjects.map((subject) => {
// if the state equals the id set to visible if not set to invisible
var view = this.state.viewDescription === subject._id ? "" : "none";
var hover = this.state.viewSummary === subject._id ? "" : "none";
var comments = this.state.comments;
return (
<div key={subject._id}>
<div
className="subjectTitle"
onClick={() => this.clickHandler(subject._id)}
onMouseEnter={() => this.hoverHandler(subject._id)}
onMouseLeave={() => this.hoverOffHandler()}
>
<p className="title">{subject.title}</p>
<p className="rating">Rating: {subject.rating}</p>
<p className="summary" style={{ display: hover }}>
{subject.summary}
</p>
</div>
<div className="subjectBody " style={{ display: view }}>
<div className="subjectAuthor">
<p className="author">
Subject created by: {subject.author} on {subject.date}
</p>
<a href="">
<div className="buttonRateSubject">RATE SUBJECT</div>
</a>
</div>
<div className="subjectDescription">
<p className="description">{subject.description}</p>
</div>
<div className="subjectLinks">Links:</div>
<div className="subjectComments">
<p>Comments:</p>
{/* ************HERE*********** */}
<p>{comments}</p>
{/* ********************************* */}
<a href="/addcomment">
<div className="buttonAddComment">ADD COMMENT</div>
</a>
</div>
</div>
</div>
);
});
return (
<div id="Subject">
<GoogleSearch />
{subjectItems}
</div>
);
}
}
Subject.propTypes = {
fetchSubjects: PropTypes.func.isRequired,
fetchComments: PropTypes.func.isRequired,
subjects: PropTypes.array.isRequired,
comments: PropTypes.array.isRequired,
newPost: PropTypes.object,
};
const mapStateToProps = (state) => ({
subjects: state.subjects.items,
newSubject: state.subjects.item,
comments: state.comments.items,
newComment: state.comments.item,
});
// export default Subject;
export default connect(mapStateToProps, { fetchSubjects, fetchComments })(
Subject,
Comment
);
I think I know your problem. You want to render items of an array.
Let me just give you a short overview.
Javascript:
this.setState({
comments: data
});
render (){
return (
<div>
{ this.state.comments.map(c=> <div>{c.body}</div> ) }
</div>
)
}
Thanks guys, i changed the for loop in the clickHandler to this which now has data rendering, it didn't like objects in the array for some reason.
var temp = [];
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
temp.unshift(this.props.comments[i].comment);
temp.unshift(this.props.comments[i].title);
}
}
this.setState({
comments: temp,
});

ReactJS rendering issue with edited array

Why does ReactJS remove the last element when the array is different after removing the middle element when using array.splice?
This is my code. I am using React-Redux.
const reducerNotesAndLogin = (state = initialState, action) => {
var tableNotes = "notities";
var tableCategories = "categories";
switch(action.type){
case "CATEGORY_REMOVE":
// Remove the category
var newCategories = state.categories;
console.log("state.categories", state.categories);
console.log("before: ", {newCategories});
var index = 0;
for(var i = 0; i < newCategories.length; i++){
if(newCategories[i].id === action.payload.categoryId){
newCategories.splice(i, 1);
index = i;
i--;
}
}
console.log("after: ", {newCategories});
state = {
...state,
categories: newCategories
}
break;
default:
break;
}
return state;
}
export default reducerNotesAndLogin;
Output below (I deleted the middle element. My web app always removes the last element of the categories (but not from the array).
Step 1: Initial state
Step 2: Remove middle item, expecting the middle item to be removed.
Step 3: Confusion
Why is the array correct, but the view incorrect? I am updating the state.categories correctly right?
This is my render code (as is - without filtering away any other code that mihgt be important)
CategoriesBody:
import React from 'react';
import { connect } from 'react-redux';
import CategoryItem from './CategoryItem';
import Button from './../../Button';
import store from '../../../redux/store-index';
class CategoriesBody extends React.Component {
render(){
return (
<div>
<ul className="list--notes">
{this.props.categories.map((category) => {
if(category.id === undefined){ // No categories
return <li>No categories</li>
} else {
return (
<div>
<CategoryItem category={category} />
<div className="mb-small hidden-sm hidden-md hidden-lg"> </div>
</div>
);
}
})}
</ul>
</div>
);
}
}
function mapStateToProps(state){
return {
categories: state.reducerNotesAndLogin.categories,
categoriesLength: state.reducerNotesAndLogin.categories.length
};
}
export default connect(mapStateToProps)(CategoriesBody);
CategoriesItem.js:
import React from 'react';
import store from './../../../redux/store-index';
import Button from './../../Button';
class CategoryItem extends React.Component {
constructor(props){
super();
this.state = {
edit: false,
categoryName: props.category.categoryName,
categoryColor: props.category.categoryColor
}
this.onClickEdit = this.onClickEdit.bind(this);
this.onChangeCategoryColor = this.onChangeCategoryColor.bind(this);
this.onChangeInputCategoryName = this.onChangeInputCategoryName.bind(this);
this.onClickEditSave = this.onClickEditSave.bind(this);
this.onClickEditCancel = this.onClickEditCancel.bind(this);
}
removeCategory(id, name){
console.log("nsvbsvbfjvbdjhbvv");
store.dispatch({ type: "CATEGORY_REMOVE", payload: {
categoryId: id
}});
// store.dispatch({type: "NOTIFY", payload: {
// type: 'success',
// message: 'Category "' + name + '" removed!'
// }});
}
onClickEdit(){
this.setState({
edit: true
});
}
onChangeCategoryColor(e){
this.setState({
categoryColor: e.target.value
});
}
onChangeInputCategoryName(e){
this.setState({
categoryName: e.target.value
});
}
onClickEditSave(){
this.setState({
edit: false,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
});
store.dispatch({type: "CATEGORY_EDIT", payload: {
categoryId: this.props.category.id,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
}});
store.dispatch({type: "NOTIFY", payload: {
type: "success",
message: "Category saved!"
}});
}
onClickEditCancel(){
this.setState({
edit: false,
categoryName: this.props.category.categoryName,
categoryColor: this.props.category.categoryColor
});
}
render(){
return (
<li key={this.props.category.id} className={this.state.edit === true ? "mt mb" : "flex-justify-between flex-align-center"}>
<div className={this.state.edit === true ? "d-none" : ""}>
<div className="input--color" style={{
backgroundColor: this.state.categoryColor
}}> </div>
{this.state.categoryName}
</div>
{/* Mobile */}
<div className={this.state.edit === true ? "d-none" : "hidden-sm hidden-md hidden-lg"}>
<Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button>
<div className="mt-small"> </div>
<Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button>
</div>
{/* Tablet and desktop */}
<div className={this.state.edit === true ? "d-none" : "hidden-xs"}>
<div style={{float:'left',}}><Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button></div>
<div style={{float:'left',marginLeft:'15px'}}><Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button></div>
</div>
{/* EDITING STATE */}
<div className={this.state.edit === true ? "" : "d-none"}>
<div className="row">
<div className="col-xs-12">
<input onChange={this.onChangeCategoryColor} className="input--wide" type="color" value={this.state.categoryColor}
style={{backgroundColor: this.state.categoryColor, height: '30px'}}
/>
<input onChange={this.onChangeInputCategoryName} className="input--wide" type="text" value={this.state.categoryName} />
</div>
</div>
<div className="row mt">
<div className="col-xs-12">
<Button buttonType="primary" onClick={() => this.onClickEditSave()}>Save</Button>
</div>
</div>
<div className="row mt-small">
<div className="col-xs-12">
<Button buttonType="secondary" onClick={() => this.onClickEditCancel()}>Cancel</Button>
</div>
</div>
</div>
</li>
)
}
}
export default CategoryItem;
I think it has something to do with the rendering. Because the arrays are correct when I console.log them. Only the view is different...
Do not modify the state in reducer directly. Create a copy of state value and then modify it.
Change:
var newCategories = state.categories;
To:
var newCategories = [...state.categories];
You should not modify the same array while looping through it.
for (var i = 0; i < newCategories.length; i++) {
if (newCategories[i].id === action.payload.categoryId) {
newCategories.splice(i, 1);
index = i;
i--;
}
}
I got the answer after looking through it with a friend of mine. The solution is pretty simple...
Lesson 101: Make sure that you have a unique "key" property when looping through an array in your UI.
The solution is to add this to my code:
<div key={category.id}>
{this.props.categories.map....
...
</div>

Setting state of nested array with React Hooks

I have been working with React Hooks for a while, but the biggest problem for me is working with arrays.
I am making a register form for teams. Teams have players (array of strings).
The user should be able to add a team, and for each team, an input is shown with the current members in the team displayed above the input.
My question: How do I set the state of a nested array with React Hooks?
On the button click, it should (for now) add a string to the players array of the current team.
My Code:
interface ITeam {
id: string;
players: Array<string>;
}
export default function Team() {
const [teams, setTeams] = useState<Array<ITeam>>([{id: '1', players: ['a', 'b']}]);
return (
<div>
{teams.map((team, teamIndex) => {
return (
<div key={teamIndex}>
<h2>Team {teamIndex + 1}</h2>
<ul>
{team.players.map((player, playerIndex) => {
return (
<div key={playerIndex}>
{player}
</div>
);
})}
</ul>
<button onClick={() => setTeams([...teams, team.players.concat('c')])}>Add player</button>
</div>
);
})}
</div>
);
}
You need to make use of team index and update that particular teams value using spread syntax and slice like
function addPlayer(index) {
setTeams(prevTeams => {
return [ ...prevTeams.slice(0, index), {...prevTeams[index], players: [...prevTeams[index].players, "c"] }, ...prevTeams.slice(index+1)];
});
}
or better you can just use map to update
function addPlayer(index) {
setTeams(prevTeams => {
return prevTeam.map((team, idx) => {
if(index === idx) {
return {...prevTeams[index], players: [...prevTeams[index].players, "c"]}
} else {
return team;
}
})
});
}
const { useState } = React;
function Team() {
const [teams, setTeams] = useState([{ id: "1", players: ["a", "b"] }]);
function addPlayer(index) {
setTeams(prevTeams => {
return [ ...prevTeams.slice(0, index), {...prevTeams[index], players: [...prevTeams[index].players, "c"] }, ...prevTeams.slice(index+1)];
});
}
return (
<div>
{teams.map((team, teamIndex) => {
return (
<div key={teamIndex}>
<h2>Team {teamIndex + 1}</h2>
<ul>
{team.players.map((player, playerIndex) => {
return <div key={playerIndex}>{player}</div>;
})}
</ul>
<button onClick={() => addPlayer(teamIndex)}>Add player</button>
</div>
);
})}
</div>
);
}
ReactDOM.render(<Team />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

React & Redux: "form submission canceled because the form is not connected"

I am still new in React and Redux. So, I know the existence of redux-form, but Im not intend to use in this project. So, what I am doing is creating a form without using redux-form. This form will grab the data from the reducers and pass it to backend API.
This is my main CreateListing.jsx page.
// #flow
import React from 'react';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { SEOService } from '[services]';
import CreateListingFormPage1 from './CreateListing/CreateListingFormPage1';
import CreateListingFormPage2 from './CreateListing/CreateListingFormPage2';
import CreateListingFormPage3 from './CreateListing/CreateListingFormPage3';
import WhereAmI from './CreateListing/WhereAmI';
import SuccessCreateListing from './CreateListing/SuccessCreateListing';
type Props = {
...props...
};
class CreateListing extends React.Component<Props> {
getPageBySequence(pagenum) {
// depending on whether User is logged in or not, show/hide the Login/Signup form which is Page3
let sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage3 ];
if (this.props.isLoggedIn) {
sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage2 ];
}
return sequence[pagenum-1];
}
getSubmitCreateListing = (e) => {
e.preventDefault();
const propertyType = this.props.listingType;
const propertyName = this.props.suggestedBuildings.selected;
const propertyBuildingType = this.props.propertyBuildingType;
const bedrooms = this.props.bed;
const bathrooms = this.props.bath;
const price = this.props.price;
const builtUp = this.props.builtUp;
const title = this.props.title;
const tenure = this.props.tenure;
const description = this.props.description;
/* IN CASE USER NOT YET LOGGGED IN */
if(this.props.isLoggedIn === false) {
const email = this.props.email;
const password = this.props.password;
this.props.cacheCreateListing({ email, password, propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
}
this.props.cacheCreateListing({ propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
if(CreateListingFormPage1.landedTypes.includes(propertyBuildingType)) {
this.props.geocodingRequired(true);
}
else {
this.props.geocodingRequired(false);
}
this.props.onSubmitCreateListing();
}
onAuthenticateAndCreateListingButton() {
if(this.props.isLoggedIn) {
return(
<div role="presentation">
<div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
<div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}>
<button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>
</div>
</div>
)
}
return <div className={`column ${this.props.formCurrentPage < 3 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
}
render() {
if(this.props.isListingCreated){
return <SuccessCreateListing />;
}
else if(this.props.isListingLoading){
return <div className="create-listing-spinner" />
}
const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
return (
<div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}>
<Helmet>
<title>{ SEOService.getMetaTitle('Create Property Listing') }</title>
{ SEOService.getCanonicalTag('/blogs') }
</Helmet>
<section className="CreateListing--Main">
<div className="CreateListing--Container">
<div className="CreateListing--WhereAmI">
<WhereAmI page={this.props.formCurrentPage} />
</div>
<div className="CreateListing--Body">
<form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }>
<CurrentPage />
<div className='columns'>
<div className='column'/>
{/* CHANGE THIS this.props.formCurrentPage < 3 later */}
<div className={`column ${this.props.formCurrentPage > 1 && this.props.formCurrentPage < 4 ? '':'displayNone'}`}>
<button type="button" className="Button button-back" onClick={this.props.onPrevClick}>
BACK
</button>
</div>
{ this.onAuthenticateAndCreateListingButton() }
<div className='column'/>
</div>
</form>
</div>
</div>
</section>
</div>
);
}
};
const MapStateToProps = (state: State) => ({...});
const MapDispatchToProps = (dispatch: Dispatch) => ({
onLoadingCreateListing: () => dispatch({type: 'CREATE_LISTING_LOADING'}),
onSubmitCreateListing: () => dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})
export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);
So, my <input type="text" /> are all from CreateListingFormPage1, CreateListingFormPage2 and CreateListingFormPage3 and put together in <CurrentPage />. My <form onSubmit={...}></form> is in this CreateListing.jsx page. Im not whether it is allowed to do it like this.
So, when I click submit, I got warning of Form submission canceled because the form is not connected.
My example of <input type="" /> in CreateListingFormPage1 are:
// #flow
import React from 'react';
import { connect } from 'react-redux';
import {Dropdown} from '[elements]';
type Props = {...props...};
class CreateListingFormPage2 extends React.Component<Props> {
static get selectTenure() { return ["Select Tenure"].concat(this.tenureTypes) };
static get selectTitle() { return ["Select Title"].concat(this.titleTypes) };
static get selectBedroom() { return["Select Bedrooms no"].concat(this.bedroomNo) };
static get selectBathroom() { return["Select Bathrooms no"].concat(this.bathroomNo) };
static get tenureTypes(){
return[
"FREEHOLD",
"LEASEHOLD",
"OTHERS"
]};
static get titleTypes(){
return[
"RESIDENTIAL",
"COMMERCIAL",
"INDUSTRIAL"
]};
static get bedroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
static get bathroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
get selectTenure() { return this.constructor.selectTenure; }
get selectTitle() { return this.constructor.selectTitle; }
get selectBedroom() { return this.constructor.selectBedroom; }
get selectBathroom() { return this.constructor.selectBathroom; }
get tenureTypes() { return this.constructor.tenureTypes; }
get titleTypes() { return this.constructor.titleTypes; }
get bedroomNo() { return this.constructor.bedroomNo; }
get bathroomNo() { return this.constructor.bathroomNo; }
hasInputError = (name) => {
if (this.props.errors[name]) {
return ' is-error';
}
return '';
}
render() {
return (
<div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'>
{/* <form className="Listing--form"> */}
<div className="Listing--bedrooms-bathrooms">
<div className="type-title">No. of Bedrooms</div>
<Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/>
<div className="type-title">Asking Price</div>
<input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) => this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/>
</div>
<div className="Listing--price-built-up">
<div className="type-title">No. of Bathrooms</div>
<Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/>
<div className="type-title">Built-up Size</div>
<input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) => this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/>
</div>
<div className="Listing--tenure">
<div className="type-tenure">Select Tenure</div>
<Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/>
</div>
<div className="Listing--title">
<div className="type-title">Select Title</div>
<Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/>
</div>
<div className="Listing--photos">
<div className="type-title">Upload Photos</div>
<button className={`text-input photos-input${ this.hasInputError('photos')}`}>Click to upload</button>
</div>
<div className="Listing--description">
<div className="type-title">Describe your property</div>
<textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) => this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/>
</div>
</div>
);
}
};
const MapStateToProps = (state: State) => ({
...
});
const MapDispatchToProps = (dispatch: Dispatch) => ({
...
})
export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);
Basically, there is nothing wrong with my redux store. All the value of the input is captured successfully. The problem is when submitting the form, either the onSubmit or my form structure method is incorrect.
This is CreateListing.js reducer should it be helpful:
const INITIAL_STATE= {
isListingLoading: false,
isListingCreated: false,
}
const CreateListing = (state = INITIAL_STATE, action) => {
switch(action.type){
case 'CREATE_LISTING_LOADING':
return Object.assign({}, state, {isListingLoading: true});
case 'CREATE_LISTING_SUBMIT_FORM':
return Object.assign({}, state, {isListingCreated: true});
default:
return state;
} }
export default CreateListing;
Any help is much appreciated.
if you have any other buttons in your form you should add type="button".
so make changes like this.
<button type="button" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>

React not re-rending DOM after state update

I'm having an issue re-rendering the DOM after a state change, Here is the code for the component:
<ul>
{ this.state.musicLibraryItems }
</ul>
I have a filter function that takes in a value from a text input and and filters the musicLibraryItems based on if the title has the value from the text input, I am doing that in the following function:
filterMusicLibrary(value) {
let musicLibraryItems = [];
this.state.musicGet.map(music => {
if(music.title.includes(value)) {
console.log(music.title)
musicLibraryItems.push(
<MusicLibraryItem key= {music.id} music={music} history={this.props.history}/>
);
}
});
console.log(musicLibraryItems, this.state.musicLibraryItems)
this.setState((prevState) => {
return {
musicLibraryItems: musicLibraryItems
})
}
})
}
I think the problem is that I am not changing the reference to the state variable so it isn't realizing that the state has been changed. I was wondering what the best way to mutate the state variable so that the values in the musicLibraryItems is put into this.state.musicLibraryItems so that it can be rendered on the page.
you could try
<ul>
{ this.filterMusicLibrary() }
</ul>
and change the filter to not change the state
filterMusicLibrary() {
let musicLibraryItems = [];
this.state.musicGet.map(music => {
if(music.title.includes(this.state.title)) {
console.log(music.title);
musicLibraryItems.push(
<MusicLibraryItem key= {music.id} music={music} history={this.props.history}/>
);
}
});
console.log(musicLibraryItems, this.state.musicLibraryItems);
return musicLibraryItems;
}
You shouldn't store jsx elements on your state. If you are concerned about re-calculation of the list, try reselect
More like what #Murilo already mentioned, for filters you wouldnt want to add them to state, is more calculated state...just added a working sample
const MusicLibraryItem = ({music}) => {
return <li>{music.title}</li>
}
const lotMusic = [{title: 'Awesome', id: '12121'}, {title: 'Kitchen', id: '121'},
{title: 'Golden', id: '21'}, {title: 'Beach', id: '121'}];
class App extends React.Component{
constructor(){
super()
this.state = {musicGet: lotMusic,filterValue: ''}
this.updateFilter = this.updateFilter.bind(this);
}
updateFilter(event) {
const name = event.target.name;
this.setState({[name]: event.target.value});
}
filterMusicLibrary(value) {
let musicLibraryItems = [];
this.state.musicGet.map(music => {
if(music.title.includes(value)) {
console.log(music.title)
musicLibraryItems.push(
<MusicLibraryItem key= {music.id} music={music} history={this.props.history}/>
);
}
});
return musicLibraryItems;
}
render(){
return (
<div>
<h2>Test </h2>
<input type="text" onChange={this.updateFilter}
name="filterValue"
value={this.state.filterValue}/>
<ul>{this.filterMusicLibrary(this.state.filterValue)}</ul>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app">
</div>

Categories