Cancel to retain previous state is not working - reactjs

I have apply and cancel button with list of checkeboxes.
Once apply is clicked the selected/checked value should get stored in state prevVal variable.
On seleting some other check box and clicking cancel should make checkbox to get populated with prevVal
But on clicking cancel currentValue is getting populated.
I am using a temporary variable to hold the current state on handle checkbox event.
After I push value to a temporary variable my state is getting auto updated with the temporary value.
Could anyone help me with this?

I think this is what youre looking for :)
Also here is the sandbox if you want to see the functionality: https://stackblitz.com/edit/react-pmdgir?file=index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
prevVal: [],
currentVal: [],
filter: [
{
key: 'a'
},
{
key: 'b'
},
{
key: 'c'
}
]
}
}
toggleCurrentVal = (key) => {
const currentVal = this.state.currentVal
const beforeSlicedVal = []
if(currentVal.includes(key)){
currentVal.splice(currentVal.indexOf(key), 1)
} else {
currentVal.push(key)
}
this.setState((prevState) => {
const prevStateValue = prevState.prevVal.length <= currentVal.length ? prevState.currentVal.filter((val) => val !== key) : prevState.prevVal
return {
currentVal: currentVal.sort(),
prevVal: prevStateValue
}
}, () => console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal ))
}
applyFilter = () => {
console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal )
}
cancel = () => {
this.setState({
currentVal: [],
prevVal: this.state.currentVal
}, () => console.log("currentValue:"+ this.state.currentVal + " preValue: " + this.state.prevVal ))
}
render() {
let checkboxes = [];
let checkedValues = this.state.currentVal
if (this.state.filter) {
this.state.filter.map(data => {
checkboxes.push(
<div><input type="checkbox" checked={checkedValues.includes(data.key)}name="a" value={data.key} onClick={() => this.toggleCurrentVal(data.key)}></input></div>
)
})
}
return (
<div>
{checkboxes}
<button onClick={this.applyFilter}>Apply</button>
<button onClick={this.cancel}>Cancel</button>
</div>)
}
}
render(<App />, document.getElementById('root'));

Related

how to create refs for content that gets created later

I have a component that fetches data from an api upon user input. This data then gets rendered onto the screen as <li/> tags. I want those <li/> tags to have a ref.
I tried creating an object of refs that I create after the data is fetched:
this.singleRefs = data.reduce((acc, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
and then later assign these refs to the <li/> tag: <li ref={this.singleRefs[element.id]}>
but when I print them out I always have {current:null} Here is a demo
what am I doing wrong?
With dynamic ref data, I'd propose that you should use callback refs.
import React from "react";
import "./styles.css";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.singleRefs = {};
}
componentDidMount() {
const data = [
{ value: "val1", id: 1 },
{ value: "val2", id: 2 },
{ value: "val3", id: 3 }
];
this.myFunc(data);
//you don't need this anymore
// this.singleRefs = data.reduce((acc, value) => {
// acc[value.id] = React.createRef();
// return acc;
// }, {});
}
myFunc = async (data) => {
await sleep(3000);
this.setState({ data });
};
renderContent() {
return this.state.data.map(
function (element, index) {
return (
<li key={index} ref={(node) => (this.singleRefs[element.id] = node)}>
{element.value}
</li>
);
}.bind(this)
);
}
render() {
console.log(this.singleRefs);
return <ul>{this.renderContent()}</ul>;
}
}
Sandbox

React Native : Dynamically Created TextBox value is not getting updated

I'm little new to React Native. I have a scenario where I need to create the TextInput dynamically and bind values it from an array. Once the array updates, the value of the TextInput is not updating. Below is my code.
constructor(props) {
super(props);
this.state = {
textInputValues: [],
textInput: [],
samplearray://gets an array from the JSON
}
componentDidMount() {
this.setTextInputValue();
this.prepareTextBox();
}
setTextInputValue() {
let textInputValues = this.state.textInputValues;
this.state.samplearray.map(() => {
textInputValues.push("") //default value
this.setState({ textInputValues })
})
}
prepareTextBox() {
let textInput = this.state.textInput;
this.state.samplearray.map((value, index) => {
textInput.push(<TextInput style={styles.textBox} value={this.state.textInputValues[index]} key={index} />);
})
this.setState({ textInput })}
Code to render the TextBox in the render method.
{ this.state.textInput.map((value, index) => {
return value
})}
I have button on which this.state.textInputValues array value gets changed. But change of that is not being reflected in the TextInput. Stuck with this since 2 days. Any help is appreciated, thanks in advance.
This is how your code block should look (do read the comments for explanation):
componentDidMount() {
this.setTextInputValue();
// call the below function from `setTextInputValue` as you have dependency on that
// this.prepareTextBox();
}
setTextInputValue() {
let textInputValues = [...this.state.textInputValues];
this.state.samplearray.map((value) => {
textInputValues = [ ...textInputValues , value] //default value
// this is how you should call `prepareTextBox`
// in setState callback as it will confirm that state is updated
this.setState({ textInputValues },() => {
this.prepareTextBox();
})
})
}
prepareTextBox() {
let textInput = [...this.state.textInput];
this.state.samplearray.map((value, index) => {
textInput.push(<input value={this.state.textInputValues[index]} key={index} />);
})
this.setState({ textInput })
}
You can run the below snippet and check, hope that will clear your doubts :
const { useState , useEffect } = React;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
textInputValues: [],
textInput: [],
samplearray:["Vivek","Darshita"]//gets an array from the JSON
}
}
componentDidMount() {
this.setTextInputValue();
}
setTextInputValue() {
let textInputValues = [...this.state.textInputValues];
this.state.samplearray.map((value) => {
textInputValues = [ ...textInputValues , value] //default value
this.setState({ textInputValues },() => {
this.prepareTextBox();
})
})
}
prepareTextBox() {
let textInput = [...this.state.textInput];
this.state.samplearray.map((value, index) => {
textInput.push(<input value={this.state.textInputValues[index]} key={index} />);
})
this.setState({ textInput })
}
changeValues = () => {
this.setState({
textInput : [],
textInputValues : ["New - Vivek" , "New - Darshita"]
},() => {
this.prepareTextBox();
});
}
render() {
return (
<div>
{ this.state.textInput }
<button onClick={this.changeValues}>Change Value</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('react-root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>

I think render works twice

I'm only learning React, trying to write a simple TODO list app. When I'm trying to add a new task, two identical tasks are added. I tried to debug by the console.log element and saw a problem. render works twice, so my button sends info to the function twice. Can someone please guide me to the solution? Here is the code.
import React from 'react';
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
}
addTask = () => {
const { input } = this.state;
if (input) {
this.props.addTask(input);
this.setState({ input: '' });
}
};
handleEnter = event => {
if (event.key === 'Enter') this.addTask();
};
inputChange = event => {
this.setState({ input: event.target.value });
};
render() {
const { input } = this.state;
console.log(this.state);
return (
<div className="task-input">
<input
type="text"
onKeyPress={this.handleEnter}
onChange={this.inputChange}
value={input}
></input>
<button onClick={this.addTask } >ADD</button>
</div>
);
}
}
export default TaskInput;
Here is the App.js code:
import React from 'react';
import Task from './components/Task';
import TaskInput from './components/TaskInput';
class App extends React.Component {
constructor () {
super();
this.state = {
tasks: [
{id: 0, title: 'Create Todo-app', done: false},
{id: 1, title: 'Do smth else', done: true},
{id: 2, title: 'Do more things', done: false}
]
};
}
addTask = task => {
this.setState(state => {
let {tasks} = state;
console.log("state");
tasks.push({
id: tasks.length !==0 ? tasks.length : 0,
title: task,
done: false
});
return tasks;
});
}
doneTask = id => {
const index = this.state.tasks.map(task => task.id).indexOf(id);
this.setState(state => {
let {tasks} = state;
tasks[index].done = true;
return tasks;
});
};
deleteTask = id => {
const index = this.state.tasks.map(task => task.id).indexOf(id);
this.setState(state => {
let {tasks} = state;
delete tasks[index];
return tasks;
})
};
render() {
const { tasks } = this.state;
const activeTasks = tasks.filter(task => !task.done);
const doneTasks = tasks.filter(task => task.done)
return (
<div className = "App">
<h1 className="top">Active tasks: {activeTasks.length}</h1>
{[...activeTasks, ...doneTasks].map(task => (
<Task
doneTask={() => this.doneTask(task.id)}
deleteTask={() => this.deleteTask(task.id)}
task={task}
key={task.id}
></Task>))}
<TaskInput addTask={this.addTask}></TaskInput>
</div>
);
}
}
export default App;
I think you are accidentally directly modifying the state inside addTask.
The line let {tasks} = state; is creating a reference to the original state, rather than a new copy, and then your push modifies the state directly.
Using expansion/spread syntax to get a copy of your array like this should work:
addTask = task => {
this.setState(state => {
const tasks = [ ...state.tasks ];
tasks.push({
id: tasks.length !==0 ? tasks.length : 0,
title: task,
done: false
});
return { tasks };
});
}
Using let tasks = [ ...state.tasks ]; will create a new array rather than a reference, and prevent the state from being modified directly.
The reason you were seeing double results was that you effectively set the state with the push, and then set it again with the returned value.
I've changed your code a little bit. It's working here. Would you please check?
class TaskInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
tasks: []
};
}
addTask = newTask => {
this.setState(state => ({
...state,
input: "",
tasks: [...state.tasks, newTask]
}));
};
handleEnter = event => {
if (event.key === "Enter") this.addTask(event.target.value);
};
inputChange = event => {
this.setState({ input: event.target.value });
};
render() {
const { input } = this.state;
console.log(this.state);
return (
<div className="task-input">
<input
onKeyPress={this.handleEnter}
onChange={this.inputChange}
value={input}
></input>
<button onClick={this.addTask}>ADD</button>
</div>
);
}
}
ReactDOM.render(<TaskInput/>, document.querySelector("#root"));
.as-console-wrapper {
max-height: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Why does this Component does not work when converted to React-Hooks? (confused about this state and destructuring part)

basically attempting to create an Autocomplete feature for a booking engine the code is in class components want to convert it to a functional component with React Hooks.Have attempted to convert but my code is showing several warnings.can provide any code snippets if needed.
(how do you convert this.state and destructure the this keyword)
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
class AutocompleteClass extends Component {
static propTypes = {
suggestions: PropTypes.instanceOf(Array)
};
static defaultProps = {
suggestions: []
};
constructor(props) {
super(props);
this.state = {
// The active selection's index
activeSuggestion: 0,
// The suggestions that match the user's input
filteredSuggestions: [],
// Whether or not the suggestion list is shown
showSuggestions: false,
// What the user has entered
userInput: ""
};
}
onChange = e => {
const { suggestions } = this.props;
const userInput = e.currentTarget.value;
// Filter our suggestions that don't contain the user's input
const filteredSuggestions = suggestions.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.value
});
};
onClick = e => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText
});
};
onKeyDown = e => {
const { activeSuggestion, filteredSuggestions } = this.state;
// User pressed the enter key
if (e.keyCode === 13) {
this.setState({
activeSuggestion: 0,
showSuggestions: false,
userInput: filteredSuggestions[activeSuggestion]
});
}
// User pressed the up arrow
else if (e.keyCode === 38) {
if (activeSuggestion === 0) {
return;
}
this.setState({ activeSuggestion: activeSuggestion - 1 });
}
// User pressed the down arrow
else if (e.keyCode === 40) {
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
this.setState({ activeSuggestion: activeSuggestion + 1 });
}
};
render() {
const {
onChange,
onClick,
onKeyDown,
state: {
activeSuggestion,
filteredSuggestions,
showSuggestions,
userInput
}
} = this;
let suggestionsListComponent;
if (showSuggestions && userInput) {
if (filteredSuggestions.length) {
suggestionsListComponent = (
<ul class="suggestions">
{filteredSuggestions.map((suggestion, index) => {
let className;
// Flag the active suggestion with a class
if (index === activeSuggestion) {
className = "suggestion-active";
}
return (
<li className={className} key={suggestion} onClick={onClick}>
{suggestion}
</li>
);
})}
</ul>
);
} else {
suggestionsListComponent = (
<div class="no-suggestions">
<em>No suggestions, you're on your own!</em>
</div>
);
}
}
return (
<Fragment>
<input
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
value={userInput}
/>
{suggestionsListComponent}
</Fragment>
);
}
}
export default AutocompleteClass;

ReactJS: filter images to map into carousel

This is my BrandsCarousel.js
import React, {Component} from 'react'
import _ from 'lodash'
import {connect} from 'react-redux'
import AliceCarousel from 'react-alice-carousel';
import { getSellerBrands } from '../../actions'
class BrandsCarousel extends Component{
state = {
brands: []
}
componentWillReceiveProps = (nextProps) => {
if(nextProps.sellerBrands !== this.props.sellerBrands){
var filterBrands = !_.isEmpty(nextProps.sellerBrands.data)
? nextProps.sellerBrands.data.data.filter((item) => {
return item.type === "brand"
})
: []
console.log("filterBrands", filterBrands)
this.setState({
brands: filterBrands
})
}
}
galleryItems() {
var checkImage = this.state.brands.length === 0 ? [] : this.state.brands.filter((item) => item.collection_name === "images")
console.log('henhen',checkImage)
return (
checkImage.map((item, i) => (
<div key={i} className="card-img-top"><img src={item.url} /></div>
))
)
};
render(){
const items = this.galleryItems();
const responsive = {
0: {
items: 4
},
600: {
items: 5
},
1024: {
items: 6
}
};
return (
<AliceCarousel
items = {items}
mouseDragEnabled
responsive={responsive}
buttonsDisabled={true}
dotsDisabled={true}
/>
)
}
}
const mapStateToProps = state => {
return {
// brandList: state.brandList,
sellerBrands: state.sellerBrands
}
}
export default connect(mapStateToProps, { getSellerBrands })(BrandsCarousel)
now, filterBrands, returns this:
but henhen does not return anything but it should return something because the response got 2 images with the collection_name = "images" yet I get nothing. why is that? I want to display all images in the carousel. what am I doing wrong here? I am new to ReactJS so please be specific and thanks in advance
I think this line is not correct :
var checkImage = this.state.brands.length === 0 ? [] : this.state.brands.filter((item) => item.collection_name === "images")
In your filter condition you are checking if item.collection_name === "images", but your item contains a media array which contains many items. I think you should instead do this:
this.state.brands.map((brand, i) => {
var checkImage = brand.media.length === 0 ? [] : brand.media.filter((media) => media.collection_name === "images");
checkImage.map((image, i) => (
...
));
})

Resources