Price filtering in reactjs - javascript

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}
/>
)
}

Related

Lifting state up in React from all fields in a form as an object

I've created a Letter component, composed of Address, LetterText and Signature.
The state is kept at the Letter level, being the parent.
The method to update the state is propagated to the children as props.
This works well for LetterText which is basically a textarea, but I can't quite get it working for the Address, given it's made of many different input tags.
LetterText looks like this:
export default function Letter(props) {
const [letter, setLetter] = useState({
address: {},
text: "",
signature: null
});
function handleChange(event) {
const value = event.target.value;
setLetter({
...letter,
[event.target.name]: value
});
}
return (
<div>
<Address name="address", letterAddress={letter.address} setLetterAddress={handleChange}/>
<LetterText name="text", letterText={letter.text}, setLetterText={handleChange}/>
<LetterSignature />
</div>
);
}
The LetterText component (working) is as follows:
export default function LetterText(props) {
const { name, letterText, setLetterText, ...rest } = props;
return (
<textarea
name={name}
value={letterText}
onChange={setLetterText}
{...rest}
>
</textarea>
);
}
The Address component is the one I'm struggling with. As you can see I thought about wrapping the setLetterAddress function and do some data massaging. However, this leads to the "
letter.address object to have a pair undefined: undefined in it. Here's my (not working attempt):
export default function LetterAddress(props) {
const { name, letterAddress, setLetterAddress, ...rest } = props;
function handleChange(event) {
const { eName, eValue } = event.target;
let address = { ...letterAddress };
address[eName] = eValue;
const e = { target: { name: name, value: address } };
setLetterAddress(e);
}
return (
<form>
<label>
Full name
<input
type="text"
name="fullName"
value={letterAddress.fullName}
onChange={handleChange}
/>
</label>
<label>
Address
<input
type="text"
name="addressLine"
value={letterAddress.addressLine}
onChange={handleChange}
/>
</label>
</form>
);
}
How can I lift the status up from the Address component nicely?

React adding/removing items to an array

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.

input filtering in reactjs

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>
);
}
}

React - Forms - How to deal with child component updates

I have a form component that has a state containing an array of items.
I am having a hard time trying to update the state of the form when one of the item inputs gets updated.
At first I was creating a state on the items themselves and updating the values using the following code:
class ItemRow extends Component{
constructor(props){
super(props)
this.state = this.props.item;
}
updateItem(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
.....
render(){
return (
<FormControl
type="text"
name="name"
value={this.state.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.state.price}
onChange={this.updateItem}
/>
.....
)
}
}
This worked fine for updating the value of the of the inputs, however the state was local to the item and not reflected or accessible by the form
I am trying to figure out how to keep the state in the form and have the item update the state of the form
I think this is the right approach but I can't figure out how to get it to work.
At this point I have something similar the following:
class Form extends Component{
this.state = {
items: [
{ name: 'soup', price: 7, quantity: 1 }
{ name: 'salad', price: 5, quantity: 2 }
]
}
updateItem(e) {
// Not sure how to handle updating
}
removeItem(item) {
let items = this.state.items;
items.splice(items.indexOf(item), 1);
this.setState({items: items})
}
render(){
return(
<ItemTable items={this.state.items} updateItem={this.updateItem} removeItem={this.removeItem} />
)
}
}
ItemTable:
class ItemTable extends Component {
removeItem(item){
this.props.removeItem(item)
}
render(){
let items = [];
this.props.items.forEach((item) => {
items.push(<ItemRow item={item} key={item.id} removeItem={this.removeItem.bind(this,item)} updateItem={this.props.updateItem}/>);
});
return(
{items}
)
}
}
ItemRow:
class ItemRow extends Component {
removeItem(item){
this.props.removeItem(item)
}
render() {
return (
<FormControl
type="text"
name="name"
value={this.props.item.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="quantity"
value={this.props.item.quantity}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.props.item.price}
onChange={this.updateItem}
/>
<Button bsStyle="warning" onClick={this.removeItem}><Glyphicon glyph="trash"/></Button>
)
}
}
You're very close to the solution.
If you need to have a state shared between components, you should have it in the most parent component that should be aware of the state (in your case the Form component).
You pass down as props the method "updateItem" from the Form to the ItemTable and then ItemRow (like you're doing)
At this stage, inside the ItemRow you can use the method by calling 'this.props.updateItem' and you can run the function defined in Form, passing some parameters, if you need to.

Looping Through Text Inputs in React

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)
}}

Categories