How to map() when state "selectedOptions2" triggered - reactjs

i need you to look for bug my code. I know it is something wrong.
import React, { Component } from "react";
import { options1, options2, questions } from "./data";
import Select from "react-select";
class Esensial extends Component {
constructor(props) {
super(props);
this.state = {
selectedOption: [],
selectedOption2: []
};
}
handleChange1 = selectedOption => {
this.setState({ selectedOption, selectedOption2: null });
};
handleChange2 = selectedOption => {
this.setState({ selectedOption2: selectedOption });
};
filteredOptions() {
return options2.filter(o => o.link ===
this.state.selectedOption.value);
}
questionsOptions() {
return questions.filter(
question => question.link === this.state.selectedOption2.value
);
}
render() {
const filteredOptions = this.filteredOptions();
const questionsOptions = this.questionsOptions();
return (
<div>
<h1>UKM Esensial</h1>
<div className="form-group">
<label>Pilih Jenis Upaya Pelayanan Kesehatan</label>
<Select
className="form-control"
isClearable={false}
onChange={this.handleChange1}
options={options1}
value={this.state.selectedOption}
/>
</div>
<div className="form-group">
<label>Pilih Variable</label>
<Select
className="form-control"
isClearable
onChange={this.handleChange2}
options={filteredOptions}
value={this.state.selectedOption2}
/>
</div>
<div>
<table className="table table-striped">
<thead>
<tr>
<th>Sub-Variabel</th>
<th>Sasaran</th>
<th>Capaian</th>
</tr>
</thead>
<tbody>
{questionsOptions.map(q => (
<tr>
<td>{q}</td>
<td>
<input type="number" />
</td>
<td>
<input type="number" />
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
}
export default Esensial;
the code will run, when selectedOption get a value it will filter() the options2, when selectedOption2 get a value it will filter() the questionsOptions, but i don't know the code still crash.. help me please..
is it wrong with the map?
may be because i don't use componentDidUpdate(){}.
somehow when i use componentDidUpdate(), it execute by itself. maybe i use firebaseauth() to authenticate the login system.

This is because of this.state.selectedOption2.value, as you are setting value of selectedOption2 in handleChange1 null. Following can be solutions:
questionsOptions() {
const {selectedOption2} = this.state;
return questions.filter(
question => question.link === selectedOption2 && selectedOption2.value || ''
);
}
From your initial state I am assuming that selectedOption2 is array so you cannot access value as selectedOption2.value you have to access by index e.g selectedOption2[0].value(assuming 0 in the required index)

Your state is a bit confusing, because you've initialized them as arrays, but you'd like to access them as objects. The error message evinces that selectedOption2 doesn't own any property called value.
The answer strongly depends on what you expect to get when the onChange method is effected on.
I don't know your exact data structures, but if you're working with arrays, then the filter functions should be something similar to this:
questionsOptions() {
return questions.filter(
question => {
this.state.selectedOption2.map(option => {
return question === option.value;
});
}
);
}
In the aforementioned example, I assume that selectedOption2 is an array of objects, therefore you should use the map function in order to find the corresponding value that is expected.
By the way, if it's a simple value - i.e. it's a string, integer etc. -, then you should leave the value property on the right side of the comparison in your example.
questionsOptions() {
return questions.filter(
question => question === this.state.selectedOption2
);
}
On the contrary, if it's an array with simple values, then:
questionsOptions() {
return questions.filter(
question => {
this.state.selectedOption2.map(option => {
return question === option;
});
}
);
}
I'd also propose to look into your data structures and think about how the state field should be initialized, since it's quite misleading and causes impediments both for the readers and you.

Related

How to grab checkboxes and put them into state?

My problem is the following: I have a table, where I generate a row with a person's name, details and details2 from an object in the DB. Next to each person there is a checkbox. I need to put those on check into an object in state and remove them from there when unchecked, because I am going to send this object on submit to the server.
How and what is the best way to do that?
I tried with current.value but I am going in circles and I don't think I have the correct idea.
<tr>
<td>
<table >
<tbody>
<tr>
<td>
<div>
<input type="checkbox"/>
<label>
data 1
</label>
</div>
</td>
<td data-title="Column 2">
data 2
</td>
<td >
<a href="#">
Edit
</a>
Delete
</a>
</td>
<td data-title="Column 5">
data 3
</td>
</tr>
</tbody>
</table>
</td>
</tr>```
You can have a handleChange function:
handleChange = ({ target }) => {
this.setState(prevState => ({
value: {
...prevState.value,
[target.name]: target.value
},
}));
And use it on the checkboxes (be sure to name each checkbox uniquely):
<input type="checkbox"
...
name="Checkbox1"
onChange={this.handleCheckboxChange}
/>
Initialize the state with something like this:
this.setState({
value: {},
});
Now, in the object value, you have values of all those checkboxes, and that object looks something like this:
value = {
Checkbox1: true,
Checkbox2: false,
...
}
You mention removing them from the state if they are unchecked. I suggest filtering:
const { value } = this.state;
const trueCheckboxesKeys = value.keys().filter(valueKey => value[valueKey]);
let trueCheckboxes = {};
for(i = 0; i < trueCheckboxesKeys.Length; i++) {
trueCheckboxes[trueCheckboxesKeys[i]] = true;
}
You can create an onChange event handler and record event.currentTarget.checked as it will return true or false. I would also recommend adding some kind of identifier to the input element so you can tell which element is being checked so you can track it correctly.
I've done something similar where a user checks a box and it updates state.
You'll need an event handler for the checkbox, plus componentDidUpdate to see if the checkbox value changed.
componentDidUpdate(prevProps, prevState) {
const { CHECKBOX_NAME } = this.state;
if (CHECKBOX_NAME !== prevState.CHECKBOX_NAME) {
if (CHECKBOX_NAME === true) {
this.setState({
// do whatever
});
} else {
this.setState({
// change back if necessary
});
}
}
}
handleCheckboxChange = event => {
const { checked } = event.target;
this.setState({
// mark box as checked in state
});
};
...
// checkbox
<input type="checkbox"
checked={this.state.CHECKBOX_NAME}
onChange={this.handleCheckboxChange}
/>
//
It depends on how you are fetching the data you render, does it come from props or do you get it from api, but generally, provided you have the users array, it could look smth like this:
const UserList = props => {
const [selectedUsers, setSelectedUsers] = useState([]);
// Your user data here
const userData = [
{
id: "1",
name: "User1",
details: {
/*details here */
}
}
];
// Check if user is selected
const isUserSelected = user => !!selectedUsers.find(u => u.id === user.id);
// Add user to selected or remove if already there
const selectUser = user => {
setSelectedUsers(users => {
if (isUserSelected(user)) {
return users.filter(u => u.id !== user.id);
}
return [...users, user];
});
};
return (
<table>
<tbody>
{userData.map(user => (
<tr key={user.id}>
<td>
<label htmlFor={`user_${user.id}`}>{user.name}</label>
<input
id={`user_${user.id}`}
type="checkbox"
checked={isUserSelected(user)}
onChange={() => selectUser(user)}
/>
</td>
</tr>
))}
</tbody>
</table>
);
};

Is there something wrong with my react parent class?

I am practicing using react to build a simple table. Here my table has three columns. (name, job, delete). There is already some data in the table. In the third column, I want to build a button so the user can click and cancel the whole row
I already fixed several bugs but the table still does not show up
const TableBody = props => {
const rows = props.fillTheData.map((row, index) => {
return (
<tr key={index}>
<td>{row.name}</td>
<td>{row.job}</td>
<td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
class App extends React.Component {
state = {
character : [ ]
};
removeCharacter = index => {
const {character} = this.state;
this.setState({
character: character.filter((element, i) => {
return i !== index;
})
});
}
handleSubmit = character => {
this.setState({character:[...this.state.character,character]})
}
render() {
return(
<div class= "container">
<Table characters = {this.state.character} removeCharacter = {this.removeCharacter} />
<Form handleSubmit = {this.handleSubmit}/>
</div>
)
}
}
class Form extends React.Component {
constructor (props) {
super( props );
this.initialState = {
name: '',
job: ''
};
this.state = this.initialState;
}
handleChange = event => {
const{name,job} = event.target;
this.setState(
{
[name]: value
}
);
}
submitForm = () => {
this.props.handleSubmit(this.state);
this.setState(this.initialState);
}
render() {
const { name, job } = this.state;
return (
<div class="container2">
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={this.handleChange} />
<label>Job</label>
<input
type="text"
name="job"
value={job}
onChange={this.handleChange}/>
</form>
<input
type="button"
value="Submit"
onClick={this.submitForm} />
</div>
);
}
}
export default Form;
class Table extends React.Component {
render(){
const {characters, removeCharacter} = this.props;
return(
<table>
<TableHeader />
<TableBody fillTheData = {characters} removeCharacter= {removeCharacter} />
</table>
)
}
}
const TableHeader = () => {
return (
<thead>
<tr>
<th>Name</th>
<th>Job</th>
<th>Delete</th>
</tr>
</thead>
);
}
Right now, we have a cool Table component, but the data is being hard-coded. One of the big deals about React is how it handles data, and it does so with properties, referred to as props, and with state. First, we’ll focus on handling data with props.
Then let’s move all that data to an array of objects, as if we were bringing in a JSON-based API. We’ll have to create this array inside our render().
The handleChange method of Form component is declaring a constant named job which doesn't exist in event.target object and it is setting the state with value variable which doesn't exists in that scope.
So change
const{name,job} = event.target;
to
const{name,value} = event.target;
your code will just work fine.

Component Not Updating React/Redux

I am using Redux on my app. I have an Items component connected to redux. It passes down props to individual Item components.I also have a ItemEditModal component that edits the individual component. The problem i'm facing is that when i edit an Item through the ItemEditModal, the Items component does not update eventhough the redux did change. Here is relevant parts of my code:
my reducer file ..
this is the action to focus on: actionTypes.SET_EDIT_SELECTED_ITEM
const reducer = (state=initialState, action) => {
switch(action.type) {
case (actionTypes.SET_EDIT_SELECTED_ITEM):
let set_edit_selected_item_arr = {...state};
set_edit_selected_item_arr = set_edit_selected_item_arr.items.map(item => {
if (item.id === action.editInfo.id ) {
item.color = action.editInfo.color;
item.size = action.editInfo.size;
item.quantity = action.editInfo.quantity;
return item;
} else {
return item;
}
});
return {
...state,
items: set_edit_selected_item_arr
};
default:
return state;
}
}
my Items Component
import Item from '../../components/Item';
import './Items.css';
class Items extends Component {
render() {
return (
<table className="Items">
<thead>
<tr className="Items__header">
<th className="item_col">{this.props.items.length} ITEMS</th>
<th className="size_col">SIZE</th>
<th className="qty_col">QTY</th>
<th className="price_col">PRICE</th>
</tr>
</thead>
<tbody>
{this.props.items.map(item => {
return (<Item item={item} key={item.name} />);
})}
</tbody>
</table>
)
}
}
const mapStateToProps = (state) => {
return {
items: state.itemRedux.items
}
}
export default connect(mapStateToProps)(Items);
my ItemEditModal component
import './ItemEditModal.css';
class ItemEditModal extends Component {
state = {
id: this.props.itemRedux.selectedItem.id,
size: this.props.itemRedux.selectedItem.size,
color: this.props.itemRedux.selectedItem.color,
quantity: this.props.itemRedux.selectedItem.quantity,
}
onUnselectItemFunc = () => {
this.props.onSetSelectedItem(null);
this.props.onSetEditItemMode(false);
}
onQuantityChange = (e) => {
//validation required
this.setState({quantity: e.target.value})
}
onSelectChange = (e) => {
this.setState({size: e.target.value})
}
onColorChange = (newColor) => {
this.setState({color: newColor})
}
onSubmitForm = (e) => {
e.preventDefault();
this.props.onSetEditSelectedItem(this.state);
this.props.onSetEditItemMode(false);
}
render() {
return (
<div className={"ItemEdit " + (this.props.itemRedux.editItemMode ? 'showModal': '')}>
<div className="ItemEdit__close">
<span onClick={this.onUnselectItemFunc}>x</span>
</div>
<div className="ItemEdit__container">
<div className="ItemEdit__info">
<h4>{this.props.itemRedux.selectedItem.name}</h4>
<h2>$ {this.props.itemRedux.selectedItem.price}</h2>
<p className="ItemEdit__styleNum">{this.props.itemRedux.selectedItem.styleNum}</p>
<p className="ItemEdit__control-color">
<span
className="ItemEdit__control-color--red"
onClick={()=> {this.onColorChange('red')}}>
</span>
<span
className="ItemEdit__control-color--green"
onClick={()=> {this.onColorChange('green')}}>
</span>
<span
className="ItemEdit__control-color--blue"
onClick={()=> {this.onColorChange('blue')}}>
</span>
</p>
<p>Color: {this.state.color}</p>
<form onSubmit={this.onSubmitForm}>
<select onChange={this.onSelectChange}>
<option value="S">small</option>
<option value="M">medium</option>
<option value="L">large</option>
</select>
<input
type="number"
maxLength="2"
defaultValue={this.props.itemRedux.selectedItem.quantity}
onChange={this.onQuantityChange}
/><br/>
<button className="btn btn-primary">EDIT</button>
</form>
Check product details
</div>
<div className="ItemEdit__img">
<img src={this.props.itemRedux.selectedItem.imgUrl} alt="shirt pic" />
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
itemRedux: state.itemRedux
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSetSelectedItem: (bool) => dispatch(actions.setSelectedItem(bool)),
onSetEditItemMode: (bool) => dispatch(actions.setEditItemMode(bool)),
onSetEditSelectedItem: (itemInfo) => dispatch(actions.setEditSelectedItem(itemInfo))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemEditModal);
Maybe i'm not seeing something. thanks in advance.
Nothing happens when I dispatch an action
Sometimes, you are trying to dispatch an action, but your view does not update. Why does this happen? There may be several reasons for this.
Never mutate reducer arguments
It is tempting to modify the state or action passed to you by Redux. Don't do this!
Redux assumes that you never mutate the objects it gives to you in the reducer. Every single time, you must return the new state object. Even if you don't use a library like Immutable, you need to completely avoid mutation
Source: https://redux.js.org/troubleshooting#nothing-happens-when-i-dispatch-an-action

Redux form: REGISTER_FIELD / UNREGISTER_FIELD get called after change or focus events

I’m using Redux Form to render and handle form events in my React app. The following things are used:
Initial values
Field arrays
Immutable.js
Material UI
Also, the field arrays are build using the initial values.
export default connect(
state => {
return {
buttonVisible,
confirm,
initialValues: Immutable.Map({
configuration_items: configuration_items,
})
}
}
)(AssetConfiguration)
The problem is that all the fields in the form get deregistered and registered on every change or focus event. Without the defaultValues, it seems to work fine though.
I’m using a React component to render the form, something like this
class ConfigurationForm extends React.Component {
renderTableBody({ fields, meta: { touched, error } }) {
return(
<tbody>
{fields.map((field, index) => {
return(
<tr key={index}>
<td>
<Field fieldIndex={index} name={`${field}.value`} id={`${field}.value`} component={this.renderItemField.bind(this)} />
</td>
</tr>
)
})}
</tbody>
)
}
render() {
return (
<form className="defaultForm" onSubmit={handleSubmit(postAssetConfiguration)}>
<FieldArray name="configuration_items" component={this.renderTableBody.bind(this)} />
</form>
)
}
}
What could be the problem here?
Thanks,
Rens
If anyone is still having issues, I had similar problem with FieldArray and my solution was to remove bind completely and put it in it's own component so that it doesn't re-render array. It has to be outside of the component that's rendering it -
const renderItemField = ({ input }) => {
return (
<div>
<input {...input} />
</div>
);
};
const renderTableBody = ({ fields, meta: { touched, error } }) => {
return(
<tbody>
{fields.map((field, index) => {
return(
<tr key={index}>
<td>
<Field fieldIndex={index} name={`${field}.value`} id={`${field}.value`} component={renderItemField} />
</td>
</tr>
)
})}
</tbody>
)
};
class ConfigurationForm extends React.Component {
render() {
return (
<form className="defaultForm" onSubmit={handleSubmit(postAssetConfiguration)}>
<FieldArray name="configuration_items" component={renderTableBody} />
</form>
)
}
}
I'm investigating a similar issue.
My guess is: you are passing in this.renderTableBody.bind(this) and this.renderItemField.bind(this) as components. Every time render is called, these will be new references, triggering an unnecessary rerender. This is considered bad practice: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md

React - toggling input in a cell of a table

I have a table where I would like it to be possible to click on a cell and for that to toggle an input that allows you to change the data in that cell. At the moment I am just rendering the input straight off the bat but ideally I don't want this. What I would really like is to show the current data in the cells and then when you click on the cell it becomes the input and allows you to edit the data.
Table Component:
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import Row from './Row.jsx';
import {createRowHistory} from '../../actions/DALIActions';
import TokenStore from '../../stores/TokenStore';
import TableDataStore from '../../stores/TableDataStore';
export default class Table extends React.Component {
state = {data: TableDataStore.getCells().historycells};
handleSubmitEvent = (e) => {
e.preventDefault();
console.log(this.state.data);
let data = this.state.data;
let dataEntriesArray = [];
for (let key in data) {
if (data.hasOwnProperty(key)) {
dataEntriesArray.push({contents: data[key]});
}
}
console.log(dataEntriesArray);
let access_token = TokenStore.getToken();
let row_id = TableDataStore.getRowId();
createRowHistory(access_token, row_id, dataEntriesArray);
};
handleChangeEvent = (value, cell) => {
let newState = this.state.data.slice(0);
console.log(newState);
newState[cell] = value;
console.log(newState);
this.setState({data: newState});
console.log(this.state.data);
};
render() {
let {data} = this.state;
return (
<div>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
<Row data={this.state.data} handleSubmitEvent={this.handleSubmitEvent} handleChangeEvent={this.handleChangeEvent} />
</tbody>
</table>
</div>
</div>
);
}
}
Row Component:
import React from 'react';
import TableDataStore from '../../stores/TableDataStore';
export default class Row extends React.Component {
render() {
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />
</td>
);
});
cells.push(
<td className="dtable-button" key={this.props.data.length}>
<button className="btn btn-primary" onClick={this.props.handleSubmitEvent}>Submit</button>
</td>
);
return (
<tr id={TableDataStore.getRowId()}>{cells}</tr>
);
}
}
Can I just simply toggle a state that will switch between a default show state (a <div>?) and the input that I already have? Or do I need to do something a little more involved?
Any help would be much appreciated!
Thanks for your time
One approach would be to create a Cell component that renders a div, but when clicked, renders an input instead.
import React from 'react';
class Cell extends React.Component {
constructor(props) {
super(props);
this.state = { editing: false };
}
render() {
const { value, onChange } = this.props;
return this.state.editing ?
<input ref='input' value={value} onChange={e => onChange(e.target.value)} onBlur={() => this.onBlur()} /> :
<div onClick={() => this.onFocus()}>{value}</div>
}
onFocus() {
this.setState({ editing: true }, () => {
this.refs.input.focus();
});
}
onBlur() {
this.setState({ editing: false });
}
}
Then you can create your table rows like this:
let cells = data.map((el, i) => (
<td key={i}>
<Cell
value={el.contents}
onChange={v => { this.props.handleChangeEvent(v, i) }}
/>
</td>
));
You can it like these.
let cells = this.props.data.map((el, i) => {
return (
<td key={i}>
{
(el.editable)?
<input type="text" className="form-control" id={el.id} defaultValue={el.contents} onChange={(e) => {
this.props.handleChangeEvent(e.target.value, i)
}} />:
<div onClick={onCellClick(i){}}>{el.contents}</div>
}
</td>
);
});
editable flag should be toggled using onCellClick(i){} & this.props.handleChangeEvent(e.target.value, i) functions. u should update the approprite state based on the index you have here

Resources