I am working on e-commerce React/Redux project, I want to make functionality by which user can display the products according to price filter, I have made two input fields in which user can type min and max price value and can display the products which lie between the price range,
the functionality is working onChange but not displaying the products between the range, it is displaying general products
can anyone help me to sort this issue, Thanks in advance, My code and screenshot is attached below
class PriceInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.values,
};
this.onValueChangeComplete = this.onValueChangeComplete.bind(this);
}
onValueChangeComplete() {
const { onValueChange } = this.props;
onValueChange(this.state.value);
}
render() {
const { currencyCode, limits } = this.props;
const { value } = this.state;
const notChanged = _.isEqual(value, limits);
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="min"
className={styles.textInput}
placeholder={formattedValue}
/>,
'filter.price-range-min'
)}
<span className={styles.between}>{I18n.getText('filter.price-aed', {}, 'To')}</span>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
onChange={this.onValueChangeComplete}
/>,
'filter.price-range-min'
)}
</div>
</div>
);
}
}
Component in which I have to used the price functionality
case 'price':
childComponent = (
<PriceInput values={facet.data}
limits={facet.data}
currencyCode={this.props.currency.code}
onValueChange={(data) => this.onSearchChange(facet.code, data)}/>
);
break;
This is not really a fix (I don't think) but maybe it can bring you closer to a solution. I made some edits to your code and placed comments where I made changes.
class PriceInput extends React.Component {
constructor(props) {
super(props);
this.state = {
// NOTE: I don't know how props.values looks. maybe this is wrong
min: props.values.min,
max: props.values.max
};
this.onValueChangeComplete = this.onValueChangeComplete.bind(this);
}
onValueChangeComplete(minOrMax, newValue) {
const { onValueChange } = this.props;
this.setState(
{[minOrMax]: newValue}, // update the property "min" or "max" with the new value
() => onValueChange(this.state) // when the state change is done, send both min and max to onValueChange
);
// onValueChange(this.state.value);
}
render() {
// not sure what "limits" are used for
// maybe you want to use an input with type="number" and
// use the attributes "min" and "max" ? https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
const { currencyCode, limits } = this.props;
const { min, max } = this.state; // this is new.
const notChanged = _.isEqual(value, limits);
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
{I18n.getComponent(
(formattedValue) =>
<input
value={ min } // this is new
type="text"
name="min"
className={styles.textInput}
placeholder={formattedValue}
onChange={ event => this.onValueChangeComplete('min', event.target.value) } // this was missing before
/>,
'filter.price-range-min'
)}
<span className={styles.between}>{I18n.getText('filter.price-aed', {}, 'To')}</span>
{I18n.getComponent(
(formattedValue) =>
<input
value={ max } // this is new
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
onChange={ event => this.onValueChangeComplete('max', event.target.value )}
/>,
'filter.price-range-min'
)}
</div>
</div>
);
}
}
Related
I learn React and I faced some problems. I thought I understand controlled components, but well, it seems otherwise. Could you explain me, why after onChange event I receive props for rest Input components? If I want add label that depends on this.pops.name it gets messy (because of props I see in console.log). I would be grateful for explanation.
import React, { Component } from "react";
class Input extends Component {
handleChange = (e) => {
this.props.onInputChange(e);
};
chooseLabel = (props) => {
let { name } = props;
console.log(name);
if (name === "cost") {
return (
<label className="label" htmlFor={name}>
How much do you earn per hour?
</label>
);
} else if (name === "salary") {
return (
<label className="label" htmlFor={name}>
How much do you want to spend?
</label>
);
} else {
return null;
}
};
render() {
console.log("this.props.nevVal", this.props.newVal);
console.log("this.props.name", this.props.name);
return (
<div>
{this.chooseLabel(this.props)}
<input
className="Input"
value={this.props.newVal}
name={this.props.name}
placeholder={this.props.name}
onChange={this.handleChange}
/>
</div>
);
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
salary: "",
cost: "",
tip: ""
};
}
handleInputChange = (e) => {
console.log("E.target.name", e.target.name);
this.setState({ [e.target.name]: e.target.value });
};
render() {
return (
<div className="App">
<Input
name="salary"
newVal={this.state.salary}
onInputChange={this.handleInputChange}
/>
<Input
name="cost"
newVal={this.state.cost}
onInputChange={this.handleInputChange}
/>
<Input
name="tip"
newVal={this.state.tip}
onInputChange={this.handleInputChange}
/>
</div>
);
}
}
Link to Codesandbox.io
When one of the inputs is changed, you update the state of your App component. And when a state of a component is updated, it re-renders, and all its children too. So all the inputs are re-rendered (and their newVal are logged to the console), even if you change the value of a single input.
I tried to illustrate it in the following snippet :
class Input extends React.Component {
handleChange = (e) => {
this.props.onInputChange(e);
};
chooseLabel = (props) => {
let { name } = props;
// console.log(name);
if (name === "cost") {
return (
<label className="label" htmlFor={name}>
How much do you earn per hour?
</label>
);
} else if (name === "salary") {
return (
<label className="label" htmlFor={name}>
How much do you want to spend?
</label>
);
} else {
return null;
}
};
render() {
console.log(`${this.props.name} input renderd`)
return (
<div>
{this.chooseLabel(this.props)}
<input
className="Input"
value={this.props.newVal}
name={this.props.name}
placeholder={this.props.name}
onChange={this.handleChange}
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
salary: "",
cost: "",
tip: ""
};
}
handleInputChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
console.log("App component and all its children rendered :")
return (
<div className="App">
<Input
name="salary"
newVal={this.state.salary}
onInputChange={this.handleInputChange}
/>
<Input
name="cost"
newVal={this.state.cost}
onInputChange={this.handleInputChange}
/>
<Input
name="tip"
newVal={this.state.tip}
onInputChange={this.handleInputChange}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Was that your question ?
I'd like to add a new input everytime the plus icon is clicked but instead it always adds it to the end. I want it to be added next to the item that was clicked.
Here is the React code that I've used.
const Input = props => (
<div className="answer-choice">
<input type="text" className="form-control" name={props.index} />
<div className="answer-choice-action">
<i onClick={props.addInput}>add</i>
<i>Remove</i>
</div>
</div>
);
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
choices: [Input]
};
}
addInput = index => {
this.setState(prevState => ({
choices: update(prevState.choices, { $splice: [[index, 0, Input]] })
}));
};
render() {
return (
<div>
{this.state.choices.map((Element, index) => {
return (
<Element
key={index}
addInput={() => {
this.addInput(index);
}}
index={index}
/>
);
})}
</div>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"));
<div id="app"></div>
<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>
I must admit this get me stuck for a while but there was a problem with how react deals with key props. When you use an index as a key it doesn't work. But if you make sure inputs will always be assigned the same key even when the list changes it will work as expected:
const Input = props => (
<div className="answer-choice">
<input type="text" className="form-control" name={props.index} />
<div className="answer-choice-action">
<i onClick={props.addInput}>add </i>
<i>Remove</i>
</div>
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
choices: [],
nrOfElements: 0
};
}
addInput = index => {
this.setState(prevState => {
const choicesCopy = [...prevState.choices];
choicesCopy.splice(index, 0, `input_${prevState.nrOfElements}`);
return {
choices: choicesCopy,
nrOfElements: prevState.nrOfElements + 1
};
});
};
componentDidMount() {
this.addInput(0);
}
render() {
return (
<div>
{this.state.choices.map((name, index) => {
return (
<Input
key={name}
addInput={() => {
this.addInput(index);
}}
index={index}
/>
);
})}
</div>
);
}
}
Some reference from the docs:
Keys should be given to the elements inside the array to give the
elements a stable identity...
...We don’t recommend using indexes for keys if the order of items may
change. This can negatively impact performance and may cause issues
with component state.
so I am learning react js and i have stumbled upon a problem which i can't seem to solve. So i have one input that sets the number of break points, and as that number get bigger, more inputs are rendered, and those inputs are for giving a value for each 'breakpoint'. Now this is what i can't seem to figure out, if I type in for example '20' and '30' they are added to the array, no problem, however if I want to change the value of the first one(20) to a lower value, let's say 10 I can't figure out how to remove the existing 20 and replace it with a new one(10)...
Here's the codepen: https://codepen.io/anon/pen/MVZMRq
so far i have this:
class App extends React.Component {
constructor() {
super();
this.state = {
breakPointsCount: 0,
range: []
}
}
addBreakPoints(event) {
this.setState({
breakPointsCount: parseInt(event.target.value, 10),
progress: 0,
range: []
});
}
addBreakPointValue(event) {
const enteredValue = parseInt(event.target.value, 10);
const range = this.state.range.slice(0);
const breakpointsCount = this.state.breakPointsCount;
if (range.length < breakpointsCount) {
range.push(enteredValue);
}
this.setState({
range: range,
progress: 0,
});
}
render() {
const range = this.state.range;
const breaks = Array.from(Array(this.state.breakPointsCount));
return (
<div className="progress">
[{range.map((item, i) => (
<div key={item}>
<span className="break-point-value">{item}</span>
</div>
))}]
<div className="progress-options">
<label>Change count of break points (up to 10) </label>
<input type="number"
min="0"
max="10"
name="numberInput"
className="app-input"
onChange={this.addBreakPoints.bind(this)}
/>
</div>
<div className="progress-options">
<label>Change a value for each break point </label>
{breaks.map((item, i) => (
<input type="number"
key={`break-${i}`}
className="app-input"
onBlur={this.addBreakPointValue.bind(this)}
/>
))}
</div>
</div>
)
}
}
React.render(<App />, document.getElementById('app'));
You will need to keep track of which input got changed, so you would want to pass some kind of id to the input.
I recommend using a Component composition instead of binding and passing parameters to the inline handler.
You can write a small and simple Input component that all it does is getting a value and id and passing it back up onChange (or onBlur) in your case.
Then your change handler could look something similar to this:
addBreakPointValue = (value, id) => {
this.setState(({range}) => {
const nextRange = [...range];
nextRange[id] = value;
return{
range: nextRange
}
});
}
I wrote a simple example with your code, note that i changed some stuff, like using arrow functions as handlers (class members) so we can take advantage of their lexical context with this instead of binding the functions to the class.
class Input extends React.Component {
onBlur = ({ target }) => {
const { id, onBlur } = this.props;
onBlur(target.value, id);
}
render() {
const { value } = this.props;
return (
<input
type="number"
min="0"
max="10"
value={value}
onBlur={this.onBlur}
/>
)
}
}
class App extends React.Component {
state = {
breakPointsCount: 0,
range: []
}
addBreakPoints = ({ target }) => {
const val = parseInt(target.value, 10);
this.setState({ breakPointsCount: val });
}
addBreakPointValue = (value, id) => {
this.setState(({range}) => {
const nextRange = [...range];
nextRange[id] = value;
return{
range: nextRange
}
});
}
render() {
const { range, breakPointsCount } = this.state;
const breaks = Array.from(Array(breakPointsCount));
return (
<div className="progress">
<div className="progress-bar-wrapper">
<div className="progress-bar" style={{ width: `${this.state.progress}%` }} />
[{range.map((item, i) => (
<div key={item}>
<span className="break-point-value">{item}</span>
</div>
))}]
</div>
<div className="progress-options">
<label>Change count of break points (up to 10) </label>
<input type="number"
min="0"
max="10"
name="numberInput"
className="app-input"
onChange={this.addBreakPoints.bind(this)}
/>
</div>
<div className="progress-options">
<label>Change a value for each break point </label>
{breaks.map((item, i) => (
<Input type="number"
key={i}
id={i}
value={item}
className="app-input"
onBlur={this.addBreakPointValue}
/>
))}
</div>
</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>
Here is the link to fixed codepen. I have made two changes.
First in addBreakPoints method i init the range to array of the size of the input initialized with zeroes (for 5 its gonna be [0,0,0,0,0])
addBreakPoints(event) {
this.setState({
breakPointsCount: parseInt(event.target.value, 10),
progress: 0,
range: [...Array(parseInt(event.target.value, 10)).map(()=>0)]
});
}
next in render() method i bind addBreakPointValue to pass the index of the element to be updated.
{breaks.map((item, i) => (
<input type="number"
key={`break-${i}`}
className="app-input"
onBlur={this.addBreakPointValue.bind(this,i)}
/>
))}
and finally in addBreakPointValue i only update the desired element (map function return same value for all indexes except new value for the index that is passed as parameter to addBreakPointValue)
addBreakPointValue(index, event) {
const enteredValue = parseInt(event.target.value, 10);
this.setState((prevState) => ({range : prevState.range.map((r,i) => {
if(i != index) return r
return enteredValue
})}))
}
Hope it is helpful for you.
Edit: Although it now updates values, there is still issues with this code. I will propose a complete solution later.
I am working on e-commerce React/Redux project, I want to make functionality by which user can display the products according to price slider, I have made two input fields in which user can type min and max price value,This functionality is working on button click, but I want on change event, As the user type second value it filters product onChange,
can anyone help me to sort this issue, Thanks in advance, My code and screenshot is attached below
class PriceInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.values,
};
this.onValueChangeComplete = this.onValueChangeComplete.bind(this);
}
onValueChangeComplete() {
const { onValueChange } = this.props;
onValueChange(this.state.value);
}
render() {
const { currencyCode, limits } = this.props;
const { value } = this.state;
const notChanged = _.isEqual(value, limits);
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="min"
className={styles.textInput}
placeholder={formattedValue}
/>,
'filter.price-range-min'
)}
<span className={styles.between}>{I18n.getText('filter.price-aed', {}, 'To')}</span>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
/>,
'filter.price-range-min'
)}
</div>
</div>
);
}
}
Component in which I have to used the price functionality
case 'price':
childComponent = (
<PriceInput values={facet.data}
limits={facet.data}
currencyCode={this.props.currency.code}
onValueChange={(data) => this.onSearchChange(facet.code, data)}/>
);
break;
It's gonna be something like:
handleMaxChange(event) {
const minValue = '' // I'm assuming you already have max's value saved somewhere
const { value } = event.target
if (minValue && value) {
this.props.onValueChange(/* send values here */)
}
}
render() {
return (
...
<input
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
onChange=={handleMaxChange}
/>
)
}
I'm building an app where I want the user to specify a number of text fields and then render this amount of fields dynamically. I'm having trouble setting up the state so that it is unique for each field. Here is the code segment:
var fieldsArray = [];
for(var i = 0; i <= this.props.numToShow; i ++){
fieldsArray.push(
<div>
<label>
<div className="label">{i}</div>
<input type='text' value={this.state.value} name={this.state.value} onChange={this.handleChange} />
</label>
</div>
);
}
return (
<div className = 'inputs'>
{fieldsArray}
</div>
);
Currently, when I change one of the fields, all the other fields update with that unique fields state. Here is the handleChange function that sets the state:
handleChange: function(e){
this.setState({
value: e.target.value,
});
}
Is it possible to initialize the state as an array and keep track of the inputs that way? Or is there a better way to do this?
Keeping an array of values in state would work fine. You'll just have to make sure you're passing the index of the input so you know what to update. bind helps with this:
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
}
handleChange(i, e) {
this.setState({
values: { ...this.state.values, [i]: e.target.value }
});
}
render() {
var fieldsArray = [];
for (var i = 0; i <= this.props.numToShow; i++) {
fieldsArray.push(
<div>
<label>
<div className="label">{i}</div>
<input
type='text'
value={this.state.values[i]}
name={this.state.values[i]}
onChange={this.handleChange.bind(this, i)} />
</label>
</div>
);
}
return (
<div className = 'inputs'>
{fieldsArray}
</div>
);
}
}
onChange={(e) => {
var newPresetList = [...presetList]
newPresetList.map((_item) => {
if (_item.id === item.id) {
_item.preset_name = e.target.value
return
}
})
setPresetList(newPresetList)
}}