Give a value from a Json file to a select - reactjs

I want to give to my suggestor a value from a Json file. The name of those value are displayed in my select but when I am returning with an alert() my value it shows 'null'.
I want to use as a value the name already displayed/visible in the select.
The purpose of my application is after somebody choose a name in the select to show all the data from both database that has the same name.
This is the structure of both JSON file they all have different id, name,admin_area & country value
{
"id": 3,
"name": "Satimola",
"admin_area": "Theo",
"country": "Taiwan"
}
Of course I tried to pass my array suggestionOldData as a value but it didn't work the console said :
Failed prop type: Invalid prop value of type array supplied to Suggestor, expected string.
import React from "react";
import { Component } from "react";
import ReactDOM from "react-dom";
// IMPORT DATA FROM JSON FILE
import NewData from "../../api/data/cities.json";
import OldData from "../../api/data/historical.json";
// IMPORT PAGINATION FILE
import Pagination from "./Pagination.js";
// IMPORT MODULE TO CREATE INPUT SEARCH
import Suggestor from "ssuggestor";
// CREATE A COMPONENT THAT WILL SHOW THE NEW DATA IN A TABLE
class NewDataTable extends React.Component {
constructor() {
super();
this.state = {
NewData: NewData,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChangePage(pageOfItems) {
// update state with new page of items
this.setState({ pageOfItems: pageOfItems });
}
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
handleChange(event) {
var val = document.getElementById(Suggestor);
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log("Old Data =>", consoleOldData);
console.log("New Data =>", consoleNewData);
}
render() {
// GET DATA.NAME FROM OldData
const suggesttionOldData = OldData.map(value => value.name);
return (
<div>
<form>
<Suggestor
id="Suggestor"
list={suggesttionOldData}
onSelect={this.handleChange}
placeholder=" ..."
value={this.state.value}
/>
</form>
<nav>
<Pagination
items={this.state.NewData}
onChangePage={this.onChangePage}
/>
</nav>
<table>
<tbody>
<tr>
<th>New Data</th>
</tr>
{this.state.pageOfItems.map(item => (
<tr key={item.id}>
<td key={item.id}>{item.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default NewDataTable;
This is the suggestor class
class Suggestor extends PureComponent {
constructor(props) {
super(props);
autoBind(this);
this.input = React.createRef();
this.state = {
filtered: this.filter(props.list, props.value, false),
value: props.value,
open: false,
index: 0
};
}
componentDidMount() {
document.addEventListener('click', this._onClick);
}
componentWillUnmount() {
document.removeEventListener('click', this._onClick);
}
_onClick(event) {
if (!this.input.current.parentNode.contains(event.target)) {
this.close();
}
}
componentWillReceiveProps(nextProps) {
let value = this.state.value;
if (nextProps.value !== this.props.value && nextProps.value !== value) {
value = nextProps.value;
}
this.setState({
filtered: this.filter(nextProps.list, value, true),
value
});
}
close() {
this.setState({
open: false,
filtered: this.unfilter(),
index: 0
});
}
handleClick() {
if (this.props.openOnClick) {
if (this.state.open) {
this.close();
} else {
this.setState({ open: true, filtered: this.unfilter() });
}
}
}
handleKeyDown(e) {
const { onKey, useKeys } = this.props;
onKey(e);
if (useKeys && this.processKey(e.keyCode)) {
e.preventDefault();
}
}
processKey(code) {
const { open, index, filtered, value } = this.state;
const ssuggestions = filtered.length ? filtered : this.unfilter();
let nextIndex;
switch (code) {
case keys.ENTER:
if (open && filtered[index]) {
this.changeValue(filtered[index].word, true);
} else {
this.setState({ open: true, filtered: this.unfilter() });
}
break;
case keys.ESCAPE:
this.close();
if (!open && value) {
this.changeValue('');
}
break;
case keys.DOWN:
nextIndex = (index + open) % ssuggestions.length;
break;
case keys.UP:
nextIndex = (index || ssuggestions.length) - 1;
break;
case keys.TAB:
if (this.props.selectOnTab && open && filtered[index]) {
this.changeValue(filtered[index].word, true);
} else {
this.close();
}
default:
return false;
}
if (nextIndex !== undefined) {
this.setState({ open: true, index: nextIndex, filtered: ssuggestions });
}
return true;
}
handleItemClick({ word }) {
this.changeValue(word, true);
}
handleItemMouseEnter(index) {
this.setState({ index });
}
handleChange(e) {
e.stopPropagation();
const value = e.target.value;
this.changeValue(value);
}
remove() {
this.changeValue('', true);
}
changeValue(value, select = false) {
const { list, suggestOn, accents, onChange, onSelect } = this.props;
const filtered = this.filter(list, value);
const suggest = value.length >= suggestOn;
const open = !!filtered.length && suggest;
this.setState({ value, filtered, open }, () => {
onChange(value);
if (select) {
const suggestion = filtered.find(({ word }) => transform(accents, word) === transform(accents, value));
onSelect(value, suggestion && suggestion.item);
this.close();
}
});
}
filter(list, value, onlyMatch = true) {
const { accents, selector } = this.props;
value = transform(accents, value);
let mapped = list.map(item => {
const word = selector(item);
return {
index: transform(accents, word).indexOf(value),
word,
item
};
});
if (onlyMatch) {
mapped = mapped.filter(item => item.index !== -1);
}
return mapped;
}
unfilter() {
return this.filter(this.props.list, this.state.value, false);
}
focus() {
this.input.current.focus();
}
render() {
const { theme, style, placeholder, arrow, close, tooltip, required } = this.props;
const { open, value, index, filtered } = this.state;
const displaySuggestions = open && !!filtered.length;
return (
<div className={theme.root} onClick={this.handleClick} onKeyDown={this.handleKeyDown} style={style}>
<input
type="text"
className={theme.input}
onChange={this.handleChange}
value={value}
title={tooltip}
placeholder={placeholder}
required={required}
ref={this.input}
/>
{arrow && <span className={theme.arrow} />}
{close && value && <span className={theme.close} onClick={this.remove} />}
{displaySuggestions && (
<ul className={theme.list}>
{filtered.map((item, i) => (
<ListItem
key={item.word}
theme={theme}
item={item}
index={i}
onItemClick={this.handleItemClick}
onItemMouseEnter={this.handleItemMouseEnter}
overItem={i === index}
search={value}
/>
))}
</ul>
)}
</div>
);
}
}
Suggestor.propTypes = {
list: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object])).isRequired,
selector: PropTypes.func,
onChange: PropTypes.func,
onSelect: PropTypes.func,
onKey: PropTypes.func,
value: PropTypes.string,
openOnClick: PropTypes.bool,
selectOnTab: PropTypes.bool,
placeholder: PropTypes.string,
tooltip: PropTypes.string,
theme: PropTypes.shape({
root: PropTypes.string,
arrow: PropTypes.string,
close: PropTypes.string,
list: PropTypes.string,
item: PropTypes.string,
activeItem: PropTypes.string
}),
suggestOn: PropTypes.number,
style: PropTypes.object,
required: PropTypes.bool,
useKeys: PropTypes.bool,
accents: PropTypes.bool,
arrow: PropTypes.bool,
close: PropTypes.bool
};
Suggestor.defaultProps = {
theme: {},
selector: s => s,
onSelect: noop,
onChange: noop,
onKey: noop,
value: '',
openOnClick: true,
selectOnTab: false,
suggestOn: 1,
required: false,
accents: false,
useKeys: true,
arrow: true,
close: true
};
export default Suggestor;

So, looking at the example mentioned in the readme for ssugester package, it looks like you are passing wrong callback for the onSelect method.
So, I believe changing your code to the following way should work.
Change the Suggester component to have a function that represents the onSelect callback in a more meaningful way, for example:
<Suggestor
id;= "Suggestor";
list = {suggesttionOldData};
onSelect = {this.onSuggesterSelect};
placeholder =' ...'
value = {this.state.value}
/ >
and in your NewDataTable component, create another function called onSuggesterSelect.
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
onSuggesterSelect(value, suggestion) {
const val = value; // this should be your selected value (name) now.
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log('Old Data =>', consoleOldData);
console.log('New Data =>', consoleNewData);
}
So, with these changes your component should look something like this:
import React from "react";
import { Component } from "react";
import ReactDOM from "react-dom";
// IMPORT DATA FROM JSON FILE
import NewData from "../../api/data/cities.json";
import OldData from "../../api/data/historical.json";
// IMPORT PAGINATION FILE
import Pagination from "./Pagination.js";
// IMPORT MODULE TO CREATE INPUT SEARCH
import Suggestor from "ssuggestor";
// CREATE A COMPONENT THAT WILL SHOW THE NEW DATA IN A TABLE
class NewDataTable extends React.Component {
constructor() {
super();
this.state = {
NewData: NewData,
pageOfItems: []
};
this.onChangePage = this.onChangePage.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChangePage(pageOfItems) {
// update state with new page of items
this.setState({ pageOfItems: pageOfItems });
}
// GET INPUT VALUE , GET DATA WITH THE SAME NAME IN BOTH ARRAY, CONSOLE.LOG BOTH
handleChange(event) {
var val = document.getElementById(Suggestor);
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log("Old Data =>", consoleOldData);
console.log("New Data =>", consoleNewData);
}
onSuggesterSelect(value, suggestion) {
const val = value; // this should be your selected value (name) now.
alert(val);
const consoleOldData = OldData.find(value => value.name);
const consoleNewData = NewData.find(value => value.name);
console.log('Old Data =>', consoleOldData);
console.log('New Data =>', consoleNewData);
}
render() {
// GET DATA.NAME FROM OldData
const suggesttionOldData = OldData.map(value => value.name);
return (
<div>
<form>
<Suggestor
id="Suggestor"
list={suggesttionOldData}
onSelect={this.onSuggesterSelect}
placeholder=" ..."
value={this.state.value}
/>
</form>
<nav>
<Pagination
items={this.state.NewData}
onChangePage={this.onChangePage}
/>
</nav>
<table>
<tbody>
<tr>
<th>New Data</th>
</tr>
{this.state.pageOfItems.map(item => (
<tr key={item.id}>
<td key={item.id}>{item.name}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default NewDataTable;
I hope this works for you now.

var val = document.getElementById(Suggestor);
Change to
var val = event.target.value

Related

React-table does not show valid page number when filter is applied again

In my web application there is the table which displays '50' records on each page depending on the number of pages are present in the applied filter. Problem is, ex-> for a 1 JAN 2021 to 2 FEB 2021 there are 2 pages each displaying 50 records if I switch to 2 page it shows 2 of 2 pages and then again I change the the filter but pagination of react table shows 2 of 2 pages instead of 1 of 2 pages
While the data show of 1 pages according to the filter applied.
The whole code of component
import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ReactTable from 'react-table';
import { STORAGE_KEYS } from '../../constants/localStorageKeys';
import { PAGINATION_PROPS } from '../../constants/paginationProps';
import { APPOINTMENTS_MODAL_CONTENT } from '../../constants/status';
import {
areDemographicsEnabled,
areEmailOrTextRemindersEnabled,
areEmailRemindersEnabled,
areTextRemindersEnabled,
getAppointmentTableData,
hasPrecheckForms,
isBalanceEnabled,
isCopayEnabled,
isInsuranceEnabled,
isPrecheckEnabled,
areBroadcastEnabled
} from './AppointmentsUtils';
import BalanceExpandedCell from './tableCells/BalanceExpandedCell';
import CopayExpandedCell from './tableCells/CopayExpandedCell';
import DemographicsExpandedCell from './tableCells/DemographicsExpandedCell';
import FormsExpandedCell from './tableCells/FormsExpandedCell';
import InsuranceExpandedCell from './tableCells/InsuranceExpandedCell';
import RemindersExpandedCell from './tableCells/RemindersExpandedCell';
import selectTableHOC from 'react-table/lib/hoc/selectTable';
const SelectTable = selectTableHOC(ReactTable);
class AppointmentsTable extends React.PureComponent {
static propTypes = {
clickedOnReminderStatusesViewAll: PropTypes.func.isRequired,
error: PropTypes.bool,
fetchAppointments: PropTypes.func.isRequired,
filteredAppointments: PropTypes.array,
onRowClick: PropTypes.func.isRequired,
pageNumber: PropTypes.number,
practiceConfig: PropTypes.object,
selectAll: PropTypes.bool,
selectedAppointmentIds: PropTypes.array,
toggleAll: PropTypes.func.isRequired,
toggleSelection: PropTypes.func.isRequired,
totalPages: PropTypes.number
};
constructor(props) {
super(props);
const { practiceConfig } = props;
this.state = {
isExpanded: false,
expanded: _.fill(Array(PAGINATION_PROPS.PAGE_SIZE_VALUE), false),
displayAllColumns:
isPrecheckEnabled(practiceConfig) || areEmailOrTextRemindersEnabled(practiceConfig)
? this.getColumnsVisibilitySettings()
: true
};
this.isSelected = this.isSelected.bind(this);
this.toggleHeaderExpansion = this.toggleHeaderExpansion.bind(this);
this.toggleColumnsVisibility = this.toggleColumnsVisibility.bind(this);
this.getExpandedRowSubComponent = this.getExpandedRowSubComponent.bind(this);
this.fetchData = this.fetchData.bind(this);
this.setRef = this.setRef.bind(this);
this.getTdProps = this.getTdProps.bind(this);
this.onExpandedChange = this.onExpandedChange.bind(this);
this.onPatientNameClick = this.onPatientNameClick.bind(this);
}
componentDidUpdate(prevProps) {
const { filteredAppointments } = this.props;
if (filteredAppointments !== prevProps.filteredAppointments) {
this.setRowsExpansion(this.state.isExpanded);
}
}
getColumnsVisibilitySettings() {
let displayAllColumns = true;
const localStorageValue = localStorage.getItem(STORAGE_KEYS.DISPLAY_ALL);
if (localStorageValue != null) {
displayAllColumns = JSON.parse(localStorageValue);
}
return displayAllColumns;
}
isSelected(id) {
const { selectedAppointmentIds } = this.props;
return selectedAppointmentIds.includes(id);
}
toggleHeaderExpansion() {
const isExpanded = !this.state.isExpanded;
this.setRowsExpansion(isExpanded);
this.setState({ isExpanded });
}
setRowsExpansion(isExpanded) {
const expanded = _.fill(Array(PAGINATION_PROPS.PAGE_SIZE_VALUE), isExpanded);
if (!_.isEqual(expanded, this.state.expanded)) {
this.setState({ expanded });
}
}
toggleColumnsVisibility() {
const displayAllColumns = !this.state.displayAllColumns;
localStorage.setItem(STORAGE_KEYS.DISPLAY_ALL, displayAllColumns);
this.setState({ displayAllColumns });
}
getExpandedRowPrecheckDiv(row) {
const { practiceConfig } = this.props;
return (
<div className="expanded-row">
{areDemographicsEnabled(practiceConfig) && (
<DemographicsExpandedCell status={row.demographics} />
)}
{isInsuranceEnabled(practiceConfig) && (
<InsuranceExpandedCell
insuranceData={row.insurance}
clickedOnInsuranceDetails={() =>
this.props.onRowClick(
row._original,
APPOINTMENTS_MODAL_CONTENT.INSURANCE
)
}
/>
)}
{isCopayEnabled(practiceConfig) && <CopayExpandedCell paymentData={row.copay} />}
{isBalanceEnabled(practiceConfig) && (
<BalanceExpandedCell paymentData={row.balance} />
)}
{hasPrecheckForms(practiceConfig) && (
<FormsExpandedCell patientForms={_.get(row, 'forms', null)} />
)}
</div>
);
}
getExpandedRowSubComponent({ original: appointment, row }) {
const { clickedOnReminderStatusesViewAll, practiceConfig } = this.props;
return (
<div className="rt-tr expanded-row">
<div className="rt-td precheck-cell expander-cell" />
{isPrecheckEnabled(practiceConfig) && this.getExpandedRowPrecheckDiv(row)}
<div className="expanded-row">
{areEmailRemindersEnabled(practiceConfig) && (
<RemindersExpandedCell
appointment={appointment}
clickedOnViewAll={clickedOnReminderStatusesViewAll}
practiceConfig={practiceConfig}
type="email"
/>
)}
{areTextRemindersEnabled(practiceConfig) && (
<RemindersExpandedCell
appointment={appointment}
clickedOnViewAll={clickedOnReminderStatusesViewAll}
practiceConfig={practiceConfig}
type="text"
/>
)}
{areBroadcastEnabled(practiceConfig) && (
<div className="rt-td precheck-cell expanded-precheck-cell"></div>
)}
{areBroadcastEnabled(practiceConfig) && (
<div className="rt-td precheck-cell expanded-precheck-cell"></div>
)}
</div>
</div>
);
}
fetchData(reactTableState) {
const { pageNumber } = this.props;
// This function gets called twice on table page change. Once for the page change itself,
// and once for the data change. Don't re-fetch on data change. By the way, the table's
// page is 0-based, but the prop's is 1-based.
//
// This function also gets called on initial load. In that case, the table's page is 0,
// and the pageNumber prop is undefined.
if (pageNumber !== reactTableState.page + 1) {
this.props.fetchAppointments(reactTableState.page);
}
}
setRef(r) {
this.reactTable = r;
}
onPatientNameClick(rowInfo) {
this.props.onRowClick(rowInfo.original, APPOINTMENTS_MODAL_CONTENT.ALL_PATIENT_DATA);
}
onTextClicked = rowInfo => {
this.props.onRowClick(rowInfo.original, APPOINTMENTS_MODAL_CONTENT.BROADCASTTEXT);
};
onEmailClicked = rowInfo => {
this.props.onRowClick(rowInfo.original, APPOINTMENTS_MODAL_CONTENT.BROADCASTMAIL);
};
getTdProps(_state, rowInfo, column) {
const props = {};
if (column.id === 'patientName') {
props.onClick = () => this.onPatientNameClick(rowInfo);
}
if (column.id === 'textBroadcast') {
props.onClick = () => this.onTextClicked(rowInfo);
}
if (column.id === 'emailBroadcast') {
props.onClick = () => this.onEmailClicked(rowInfo);
}
return props;
}
onExpandedChange(_newExpanded, index) {
const expanded = this.state.expanded.slice();
expanded[index] = !expanded[index];
this.setState({ expanded });
}
render() {
const {
error,
filteredAppointments,
pageNumber = 1,
practiceConfig,
selectAll,
toggleAll,
toggleSelection,
totalPages = 1
} = this.props;
const data = error ? [] : filteredAppointments || [];
const pages = error ? 0 : totalPages;
const tableText = error
? 'Appointments are currently unavailable. Please try back later.'
: 'There are no appointments for selected filters';
const expandedRowSubComponent =
isPrecheckEnabled(practiceConfig) || areEmailOrTextRemindersEnabled(practiceConfig)
? { SubComponent: this.getExpandedRowSubComponent }
: {};
const reactTableProps = {
className: 'mf-appointments-table',
columns: getAppointmentTableData(
this.toggleHeaderExpansion,
this.toggleColumnsVisibility,
this.state.isExpanded,
this.state.displayAllColumns,
practiceConfig
),
data,
defaultPageSize: PAGINATION_PROPS.PAGE_SIZE_VALUE,
expanded: this.state.expanded,
getTdProps: this.getTdProps,
id: 'appointmentsTable',
loadingText: 'Loading...',
manual: true, // informs React Table that you'll be handling sorting and pagination server-side
minRows: 0,
nextText: '',
noDataText: tableText,
ofText: 'of',
onExpandedChange: this.onExpandedChange,
onFetchData: this.fetchData, // Request new data when things change
page: pageNumber - 1,
pages,
pageText: '',
previousText: '',
ref: this.setRef,
rowsText: '',
showPageSizeOptions: false,
showPagination: pages > 1,
showPaginationBottom: true,
sortable: false,
...expandedRowSubComponent
};
const selectTableProps = {
isSelected: this.isSelected,
selectAll,
selectType: 'checkbox',
toggleAll,
toggleSelection
};
return (
<div>
<SelectTable {...reactTableProps} {...selectTableProps} />
</div>
);
}
}
export default AppointmentsTable;
The Pagination has an input in which you can edit the page

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;

How to rerender one sibling component due to change of second sibling component

I have this structure:
<Filter>
<Departure setDeparture={this.setDeparture} />
<Destination setDestination={this.setDestination} iataDeparture={this.state.departure} />
<DatePicker setDates={this.setDates} />
<SearchButton />
</Filter>
Now, I try to rerender Destination component when I update Departure component. Unfortunatelly my code doesn't work.
I don't use redux because I don't know it yet, so I try solutions without redux.
Please, help me with this problem.
Here goes code for each component:
Filter:
import React, { Component } from 'react';
import axios from 'axios';
import Departure from './Departure';
import Destination from './Destination';
import DatePicker from './DatePicker';
import SearchButton from './SearchButton';
class Filter extends Component {
constructor(props) {
super(props);
this.state = {
departure: '',
destination: '',
startDate: '',
endDate: '',
flights: []
}
}
handleSubmit = event => {
const getFlights = `https://murmuring-ocean-10826.herokuapp.com/en/api/2/flights/from/${this.state.departure}/to/${this.state.destination}/${this.state.startDate}/${this.state.endDate}/250/unique/?limit=15&offset-0`;
event.preventDefault();
console.log(this.state.departure);
console.log(this.state.destination);
console.log(this.state.startDate);
console.log(this.state.endDate);
axios.get(getFlights)
.then(response => {
this.setState({ flights: response.data.flights });
console.log(getFlights);
console.log(this.state.flights);
this.props.passFlights(this.state.flights);
});
}
setDeparture = departure => {
this.setState({ departure: departure });
}
setDestination = destination => {
this.setState({ destination: destination });
}
setDates = (range) => {
this.setState({
startDate: range[0],
endDate: range[1]
});
}
render() {
return (
<section className='filter'>
<form className='filter__form' onSubmit={this.handleSubmit}>
<Departure setDeparture={this.setDeparture} />
<Destination setDestination={this.setDestination} iataDeparture={this.state.departure} />
<DatePicker setDates={this.setDates} />
<SearchButton />
</form>
</section>
);
}
}
export default Filter;
Departure:
import React, { Component } from 'react';
import axios from 'axios';
const url = 'https://murmuring-ocean-10826.herokuapp.com/en/api/2/forms/flight-booking-selector/';
class Departure extends Component {
constructor(props) {
super(props);
this.state = {
airports: [],
value: '',
iataCode: ''
}
}
componentDidMount() {
axios.get(url)
.then(data => {
const airports = data.data.airports;
const updatedAirports = [];
airports.map(airport => {
const singleAirport = [];
singleAirport.push(airport.name);
singleAirport.push(airport.iataCode);
updatedAirports.push(singleAirport);
return singleAirport;
});
this.setState({
airports: updatedAirports,
value: airports[0].name,
iataCode: airports[0].iataCode
});
this.props.setDeparture(this.state.iataCode);
});
}
handleChange = event => {
const nameValue = event.target.value;
const iataCode = this.state.airports.find(airport => {
return airport[0] === nameValue;
});
this.setState({
value: event.target.value,
iataCode: iataCode[1]
});
this.props.setDeparture(iataCode[1]);
}
render() {
const departureNames = this.state.airports;
let departureOptions = departureNames.map((item, index) => {
return (
<option value={item[0]} key={index}>{item[0]}</option>
);
});
return (
<div className='filter__form__select'>
<select value={this.state.value} onChange={this.handleChange}>
{departureOptions}
</select>
</div>
);
}
}
export default Departure;
Destination:
import React, { Component } from 'react';
import axios from 'axios';
const url = 'https://murmuring-ocean-10826.herokuapp.com/en/api/2/forms/flight-booking-selector/';
class Destination extends Component {
constructor(props) {
super(props);
this.state = {
routes: {},
airports: [],
value: '',
iataCode: '',
iataDestinationAirports: '',
options: []
}
}
componentDidMount() {
axios.get(url)
.then(data => {
const routes = data.data.routes;
const airports = data.data.airports;
const updatedAirports = [];
airports.map(airport => {
const singleAirport = [];
singleAirport.push(airport.name);
singleAirport.push(airport.iataCode);
updatedAirports.push(singleAirport);
return singleAirport;
});
this.setState({
routes: routes,
airports: updatedAirports,
});
})
.then(() => {
this.getNamesFromIataCode();
this.props.setDestination(this.state.iataDestinationAirports);
});
}
componentDidUpdate(prevProps) {
if (this.props.iataDeparture !== prevProps.iataDeparture) {
this.setState({ iataCode: this.props.iataDeparture });
() => this.getNamesFromIataCode();
};
}
handleChange = (event) => {
const nameValue = event.target.value;
const iataCode = this.state.airports.find(airport => {
return airport[0] === nameValue;
});
this.setState({
value: event.target.value,
iataDestinationAirports: iataCode[1]
});
this.props.setDestination(iataCode[1]);
}
getNamesFromIataCode = () => {
const iataCode = this.state.iataCode;
console.log(iataCode);
const destinationNames = this.state.routes[iataCode];
let destionationAirports = destinationNames.map(item => {
return this.state.airports.filter(el => {
return el[1] === item;
});
});
let arrayOfOptions = [];
let firstOptionIataCode = '';
let firstOptionName = '';
let destinationOptions = destionationAirports.map((item, index) => {
console.log(item);
arrayOfOptions.push(item[0]);
return (
<option value={item[0][0]} key={index}>{item[0][0]}</option>
);
});
firstOptionIataCode = arrayOfOptions[0][1];
firstOptionName = arrayOfOptions[0][0];
console.log(firstOptionIataCode);
this.setState({
options: destinationOptions,
iataDestinationAirports: firstOptionIataCode,
value: firstOptionName
});
console.log(this.state.iataDestinationAirports);
console.log(this.state.options);
return destinationOptions;
}
render() {
const selectionOptions = this.state.options;
return (
<div className='filter__form__select'>
<select value={this.state.value} onChange={this.handleChange}>
{selectionOptions}
</select>
</div>
);
}
}
export default Destination;
As Tholle mentioned, you need to lift the state up. Here's an example:
import React from "react";
import ReactDOM from "react-dom";
const A = ({ users, selectUser }) => {
return (
<React.Fragment>
<h1>I am A.</h1>
{users.map((u, i) => {
return <button onClick={() => selectUser(i)}>{u}</button>;
})}
</React.Fragment>
);
};
const B = ({ user }) => {
return <h1>I am B. Current user: {user}</h1>;
};
const C = ({ user }) => {
return <h1>I am C. Current user: {user}</h1>;
};
class App extends React.Component {
state = {
users: ["bob", "anne", "mary"],
currentUserIndex: 0
};
selectUser = n => {
this.setState({
currentUserIndex: n
});
};
render() {
const { users, currentUserIndex } = this.state;
const currentUser = users[currentUserIndex];
return (
<React.Fragment>
<A selectUser={this.selectUser} users={users} />
<B user={currentUser} />
<C user={currentUser} />
</React.Fragment>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Working example here.

display selected options in render() method in React

I am learning React and I am trying to achieve the following:
I am using Dropdown list which allows a user to select multiple items. Then I am trying to add each selected option in an array. Then I am tying to display the selected items (options) in the render method, but it is not working.
Later on I would like to convert this component into a reusable one so I can perform cascading.
So, my questions are:
1) how can I add each selected option into an array
2) iterate through the array and display items inside the render method
3) how to make this component reusable
The image below shows that the length of the array is 1, but I can't display the items. It is empty.
Below I am including the code that I have so far:
import * as React from 'react';
import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import { Dropdown, IDropdown, DropdownMenuItemType, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import './Dropdown.Basic.Example.scss';
import { IBaseProps,BaseComponent, createRef } from 'office-ui-fabric-react';
export interface IDropdownBasicExampleProps extends IBaseProps {
loadOptions: () => Promise<IDropdownOption[]>;
onChanged: (option: IDropdownOption, index?: number) => void;
}
export interface IDropdownBasicExampleState {
selectedItem?: IDropdownOption;
selectedItems: IDropdownOption[];
loading: boolean;
options: IDropdownOption[];
selectedKey: string | number;
error: string;
}
export class DropdownBasicExample extends BaseComponent<IDropdownBasicExampleProps,IDropdownBasicExampleState> {
private _basicDropdown = createRef<IDropdown>();
private selItem:IDropdownOption;
constructor(props:IDropdownBasicExampleProps,state:IDropdownBasicExampleState) {
super(props);
this.state = {
selectedItem: undefined,
selectedItems: new Array<IDropdownOption>(),
loading: true,
error: undefined,
options: undefined,
selectedKey:""
};
}
public componentDidMount(): void {
this.loadOptions();
}
private loadOptions(): void {
this.setState({
loading: true,
error: undefined,
options: undefined
});
this.props.loadOptions()
.then((options: IDropdownOption[]): void => {
this.setState({
loading: false,
error: undefined,
options: options
});
}, (error: any): void => {
this.setState((prevState: IDropdownBasicExampleState, props: IDropdownBasicExampleProps): IDropdownBasicExampleState => {
prevState.loading = false;
prevState.error = error;
return prevState;
});
});
}
public onChangeMultiSelect = (item: IDropdownOption, index): void => {
const updatedSelectedItem = this.state.selectedItems ? this.copyArray(this.state.selectedItems) : [];
if (item.selected) {
// add the option if it's checked
updatedSelectedItem.push(item.key);
} else {
// remove the option if it's unchecked
const currIndex = updatedSelectedItem.indexOf(item.key);
if (currIndex > -1) {
updatedSelectedItem.splice(currIndex, 1);
}
}
this.setState({
selectedItems: updatedSelectedItem,
//selectedItem:item
});
if (this.props.onChanged) {
this.props.onChanged(this.state.selectedItem, index);
}
console.log(this.state.selectedItems);
};
public copyArray = (array: any[]): any[] => {
const newArray: any[] = [];
for (let i = 0; i < array.length; i++) {
newArray[i] = array[i];
}
return newArray;
};
public render() {
const { selectedItem, selectedItems } = this.state;
return (
<div className="docs-DropdownExample">
<Dropdown
placeHolder="Select options"
label="Multi-Select controlled example:"
selectedKey={selectedItem ? selectedItem.key : undefined}
//selectedKeys={selectedItems}
onChanged={this.onChangeMultiSelect}
multiSelect
options={this.state.options}
/>
<div>length: {this.state.selectedItems.length}</div>
{this.state.selectedItems.map((item:IDropdownOption)=>{
return <div>key: {item.key} {item.selected} {item.text} {item.index}</div>
})}
</div>
);
}
}
First time define a state
this.state = {
checkbox_value: []
}
then define checkboxs such as:
<input onChange = { this.checkboxValue.bind(this) } value = "0" type="checkbox" />
<input onChange = { this.checkboxValue.bind(this) } value = "2" type="checkbox" />
then use the function.
checkboxValue(event){
let checked_list = this.state.checkbox_value;
let check = event.target.checked;
let value = event.target.value;
if(check){
this.setState({
checkbox_value: [...this.state.checkbox_value, value]
});
}else{
var index = checked_list.indexOf(event.target.value);
if (index > -1){
checked_list.splice(index, 1);
}
this.setState({
checkbox_value: checked_list
})
}
}
then you can show checked value in render using map() method for checkbox_value. I hope it is usefull for you

Resources