React: Nested Array Form - Input field onChange handler - reactjs

The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items

Related

How to create react form with dynamic fields fetched from api

I want to create a react form with dynamic inputs, those inputs are fetched from server.
I fetched the data and I have displayed the form but the problem was how to get the values of the inputs with the onChange method
Use a Usestate hook and assign the value fetched from the API there
eg:-
const [value,setValue] = useState(fetch());
function fetch(){
//API call...
//return the value got from the API
}
Now in form input field you can use this as value and have an onChange and change the value state.
eg:-
<input value={value.name} name="name" onChange={(e)=>handlechange(e)} />
in handlechange :
const handlechange = (e)=>{
const {name,value} = e.target;
setValue({...value,[name]:value});
};
After this you can have an useEffect, which will POST the value to the backend using another API for every changes made in the form
Here is the code sandbox link:https://codesandbox.io/s/blazing-moon-wxp4w7?file=/src/App.js
after some researches on stackoverflow here is my solution:
const [fields, setFields] = useState([{field:"", value:""}]);
useEffect(()=>{
if(fetchedData){
setFields(fetchedData.map((item)=>{
var rObj ={field:"", value:""};
rObj["field"] = item;
return rObj;
}));
}
},[fetchedData])
const handleChange = ( index, event ) => {
if(fields) {
let data = [...fields];
data[index]["value"] = event.target.value;
setFields(data);
}
}
/////////////////////////////////////////
{fetchedData.map((field, index) => (
<div key={index}>
<input
required
type="text"
name={field}
value={fields.field}
placeholder= {field}
onChange={event=>{handleChange(index, event)}}
/>
</div>
))}
Here is a sample code:
const GenerateForm() => {
const [formInputs, setFormInputs] = useState({});
const handleInputChange = event => {
const { name, value } = event.target;
setFormInputs({ ...formInputs, [name]: value });
};
return (
<form>
{formData.map(input => {
if (input.type === "text" || input.type === "number") {
return (
<div key={input.name}>
<label htmlFor={input.name}>{input.label}:</label>
<input
type={input.type}
name={input.name}
value={formInputs[input.name] || ""}
onChange={handleInputChange}
/>
</div>
);
} else if (input.type === "select" || input.type === "radio") {
return (
<div key={input.name}>
<label>{input.label}:</label>
{input.options.map(option => (
<div key={option.value}>
<input
type={input.type}
name={input.name}
value={option.value}
checked={formInputs[input.name] === option.value}
onChange={handleInputChange}
/>
{option.label}
</div>
))}
</div>
);
}
return null;
})}
</form>
);
}
const formData = [
{
name: "firstName",
label: "First Name",
type: "text"
},
{
name: "lastName",
label: "Last Name",
type: "text"
},
{
name: "age",
label: "Age",
type: "number"
},
{
name: "gender",
label: "Gender",
type: "radio",
options: [
{
value: "male",
label: "Male"
},
{
value: "female",
label: "Female"
}
]
},
{
name: "country",
label: "Country",
type: "select",
options: [
{
value: "india",
label: "India"
},
{
value: "usa",
label: "USA"
}
]
}
];

Custom nodes in react-flow; saving additional data to a node after it has been created

This is my first introduction to react-flow. I am looking to create a custom node where after creation, the user can enter information in the node and save/display it. From the react-flow documentation on custom nodes, they have a similar example where they created a TextUpdaterNode that console.logs the user input.
Instead of logging it it via console, I am looking for a way to save the information to the node itself and display it on the node. For example, if a user were to enter "24, male" into the input and hit the "enter" key, I want the node to be updated with that information.
What are the ways I can go about doing this?
What you're trying to do needs a little more than that:
You can see alive example here: https://codesandbox.io/s/dank-waterfall-8jfcf4?file=/src/App.js
Basically, you need:
Import useNodesState from 'react-flow-renderer';
Instead of basic definition of nodes, you will need to use: const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
Then, will have to define the onAdd, which looks like:
const onAdd = useCallback(() => {
const newNode = {
id: getNodeId(),
data: { label: `${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
}, [nodes, setNodes, state.name, state.age]);
You can include edit, pretty similar like:
const onEdit = () => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === editState.id) {
node.data = {
...node.data,
label: `${node.id} - ${editState.name} (${editState.age})`
};
}
return node;
})
);
};
Finally, draw the flow: <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
The whole code looks like:
import React, { useState, useCallback } from "react";
import ReactFlow, {
ReactFlowProvider,
useNodesState,
useEdgesState
} from "react-flow-renderer";
import "./styles.css";
const getNodeId = () => `randomnode_${+new Date()}`;
const initialNodes = [
{ id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
{ id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];
const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];
const FlowExample = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges] = useEdgesState(initialEdges);
const [state, setState] = useState({ name: "", age: "" });
const onAdd = useCallback(() => {
const newNode = {
id: getNodeId(),
data: { label: `${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
}, [nodes, setNodes, state.name, state.age]);
return (
<div>
Name:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onAdd}>add node</button>
<div style={{ width: "500px", height: "500px" }}>
<ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
</div>
</div>
);
};
export default () => (
<ReactFlowProvider>
<FlowExample />
</ReactFlowProvider>
);
Also, with edit:
import React, { useState, useCallback } from "react";
import ReactFlow, {
ReactFlowProvider,
useNodesState,
useEdgesState
} from "react-flow-renderer";
import "./styles.css";
const getNodeId = () => `${String(+new Date()).slice(6)}`;
const initialNodes = [
{ id: "1", data: { label: "Node 1" }, position: { x: 100, y: 100 } },
{ id: "2", data: { label: "Node 2" }, position: { x: 100, y: 200 } }
];
const initialEdges = [{ id: "e1-2", source: "1", target: "2" }];
const FlowExample = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges] = useEdgesState(initialEdges);
const [state, setState] = useState({ name: "", age: "" });
const [editState, setEditState] = useState({ id: "", name: "", age: "" });
const onEdit = () => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === editState.id) {
node.data = {
...node.data,
label: `${node.id} - ${editState.name} (${editState.age})`
};
}
return node;
})
);
};
const onAdd = () => {
const id = getNodeId();
const newNode = {
id,
data: { label: `${id} - ${state.name} (${state.age})` },
position: {
x: 0,
y: 0 + (nodes.length + 1) * 20
}
};
setNodes((nds) => nds.concat(newNode));
};
return (
<div>
Name:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onAdd}>add node</button>
<br />
Id:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, id: e.target.value }));
}}
/>
Name:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, name: e.target.value }));
}}
/>
Age:{" "}
<input
type="text"
onChange={(e) => {
setEditState((prev) => ({ ...prev, age: e.target.value }));
}}
/>
<button onClick={onEdit}>Edit node</button>
<div style={{ width: "500px", height: "500px" }}>
<ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
</div>
</div>
);
};
export default () => (
<ReactFlowProvider>
<FlowExample />
</ReactFlowProvider>
);
A more helpful example from documentation would be:
https://reactflow.dev/docs/examples/interaction/save-and-restore/
https://reactflow.dev/docs/examples/nodes/update-node/
But you have to remove all the extra information (Also, you can use it to go deeper!)
I managed to come up a solution to create such a custom node that allowed you to input, save and display information. I have tried to include relevant information and the code block I used below.
Custom Node
import { useCallback } from 'react';
import { Handle, Position} from 'react-flow-renderer';
const handleStyle = { left: 10 };
//Custom node requires props of data to be passed to it.
function CustomNode({ data }) {
let serviceType = "offered";
//This handles pressing enter inside the description
const handleKeyDown = (evt) => {
if (evt.key === "Enter") {
//Check if empty string
if (evt.target.value.length !== 0) {
//This code is because services are either offered or borrowed.
if (serviceType === "offered") {
data.serviceOffered.push(evt.target.value);
} else if (serviceType === "borrowed") {
data.serviceBorrowed.push(evt.target.value);
}
//Clearing input after pressing enter
evt.currentTarget.value = "";
}
}
};
const onChange = useCallback((evt) => {
//Update service type without pressing enter
serviceType = evt.target.value;
});
return (
<div className="text-updater-node">
<Handle type="target" position={Position.Top} />
<div>
<p>Entity</p>
<label htmlFor="text"><p className='nodeTitle'>{data.label}</p></label>
<input id="text" name="text" onKeyDown={handleKeyDown} />
<select name="type" onChange={onChange}>
<option value="offered" >Offered </option>
<option value="borrowed">Borrowed</option>
</select>
<div className="info">
{/* This is where the description information is displayed. It checks if it is empty, if not it loops through and displays it. */}
<h2>Service Borrowed</h2>
<ul>
{data.serviceBorrowed.length? data.serviceBorrowed.map(service => (<li key={service}>{service}</li>)) : <span></span>}
</ul>
<h2>Service Offered</h2>
<ul>
{data.serviceOffered.length? data.serviceOffered.map(service => (<li key={service}>{service}</li>)) : <span></span>}
</ul>
</div>
</div>
<Handle type="source" position={Position.Bottom} id="a" style={handleStyle} />
<Handle type="source" position={Position.Bottom} id="b" />
</div>
);
}
export default CustomNode;
I have a parent reactFlow component with the following code block. The important thing about this is to set the custom node type of react flow and pass in an object containing information about the nodes and edges to be rendered.
import { Fragment, useCallback, useState } from "react";
import ReactFlow, {
addEdge,
applyEdgeChanges,
applyNodeChanges,
} from "react-flow-renderer";
import initialNodes from "../data/nodes"; //This both ended up being empty file
import initialEdges from "../data/edges"; //This both ended up being empty file
import CustomNode from "./customNode";
import "./customNode.css";
//Set nodetype as Custom node, IMPORTANT!
const nodeTypes = { customNode: CustomNode };
function Flow() {
const defaultEdgeOptions = { animated: true };
//Input Elements
const [name, setName] = useState("");
const addNode = () => {
setNodes((e) =>
e.concat({
id: (e.length + 1).toString(),
data: { label: `${name}`, serviceOffered: [], serviceBorrowed: [] },
position: { x: 0, y: 0 },
type: "customNode",
})
);
};
//Nodes and edges containing information of the nodes and edges
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
//Boiler plate code for reactFlow
const onNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[setNodes]
);
const onEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[setEdges]
);
const onConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges]
);
return (
<Fragment>
<Row>
<Col lg={9}>
<ReactFlow
className="Canvas mt-1 border border-secondary rounded"
nodes={nodes} //Node information is passed here
edges={edges} //Edges information is passed here
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
defaultEdgeOptions={defaultEdgeOptions}
style={{ width: "100%", height: "80vh" }}
fitView
nodeTypes={nodeTypes}
/>
</Col>
</Row>
</Fragment>
);
}
export default Flow;
I added more information inside the data property of my node.js. It ended up being initialize as empty but this template should be helpful in understanding how I saved the information for the node. The edge followed the standard format shown on react-flow documentation.
export default [
// {
// id: '1',
// type: 'customNode',
// data: { label: 'Input Node', info: [{id:1, action:"Everything is burning"}, {id:2, action:"I'm fine"}], noOfActions:2 },
// position: { x: 250, y: 25 },
// },
];
I hope this has been useful!
Accepted answer is about modifying properties of components which is not React way. That code may break easily.
There are other ways to bring callback to custom nodes.
Put callback into node's data
This is from React flow documentation: https://reactflow.dev/docs/examples/nodes/custom-node/
setNodes([
...
{
id: '2',
type: 'selectorNode',
data: { onChange: onChange, color: initBgColor },
...
Cons: you need pay extra attention when you modify or create new nodes dynamically
or Define custom types dynamically
In this approach, you keep node data and behavior concerns separate.
I'm using TypeScript in order to show types of data we operate along the way.
First, you extend your custom node properties with your callback:
import {NodeProps} from "react-flow-renderer/dist/esm/types/nodes";
// by default, custom node is provisioned with NodeProps<T>
// we extend it with additional property
export type CustomNodeProps = NodeProps<CustomData> & {
onClick: (id: string) => void
}
function CustomNode(props: CustomNodeProps) {
return <button onClick={() => props.onClick(props.id)}>Do it</button>
}
Then you create new constructor that provides callback and put it into custom nodes mapping using memoization:
function Flow() {
const [graph, dispatchAction] = useReducer(...);
...
// useMemo is neccessary https://reactflow.dev/docs/guides/troubleshooting/#it-looks-like-you-have-created-a-new-nodetypes-or-edgetypes-object-if-this-wasnt-on-purpose-please-define-the-nodetypesedgetypes-outside-of-the-component-or-memoize-them
const nodeTypes = useMemo(() => {
return {
custom: (props: NodeProps<CustomData>) => {
return CustomNode({...props, onClick: (id: string) => {
dispatchAction({
type: 'customNodeButtonClicked',
nodeId: id,
})
}})
}
}
}, [])
return (
<>
<ReactFlow nodeTypes={nodeTypes} ... />
</>
);
}

How can i set value in nested object in React?

how can I set value from text Input to option[0].title when user input? I tried in many way and get error. Please help me to fix this issue.
const [options, setOptions] = useState({
0: {
title: "",
},
2: {
title: "",
1: {
title: "",
}, },
3: {
title: "",
},
title: "", });
let name, value;
const handleChange = (e) => {
name = e.target.name;
value = e.target.value;
setOptions({ ...options, [name]: value }); };
return ( <TextInput
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/> )
you just have to edit the title in the object:
const handleChange = (e) => {
name = e.target.name;
value = e.target.value;
setOptions({ ...options, [name]: { title: value } });
};
and if you need the previous information from the targeted name, spread within that object too:
setOptions({ ...options, [name]: { ...options[name], title: value } });
Fully working code,
const [options, setOptions] = useState({
0: { title: "" },
2: {
title: "",
1: { title: "" },
},
3: { title: "" },
title: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
options[+name].title = value;
setOptions({...options});
};
return (
<>
<input
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
<input
type="text"
name="2"
value={options[2].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
</>
);
I tried to check this on codesandbox and found a solution -
The name that you have updated here is string and the json has number keys. Which was the reason why it wouldn't update on title node. Also you never said .title = value, which is why values got directly updated to the options[0] element
export default function App() {
const [options, setOptions] = useState({
0: {
title: ""
},
2: {
title: "",
1: {
title: ""
}
},
3: {
title: ""
},
title: ""
});
const handleChange = (e) => {
const name = e.target.name,
value = e.target.value;
let updatedOptions = { ...options };
updatedOptions[Number(name)].title = value;
setOptions(updatedOptions);
};
return (
<div className="App">
<input
type="text"
name="0"
value={options[0].title}
onChange={handleChange}
required
placeholder="something"
icon="check_box_outline_blank"
/>
</div>
);
}

Cannot read property 'map' of undefined react 17

Currently, I started learning to react in the Udemy course "react-for-the-rest-of-us".
now, I'm trying to approach the state hook from the child component, and I get the above error.
my target is to add another element to the state by getting it's values from the user input
this is the parent component:
import React, { useState } from "react";
import AddPetForm from "./FormPet";
function Header(props) {
const [pets, setPets] = useState([
{ name: "Meowsalot", species: "cat", age: "5", id: 123456789 },
{ name: "Barksalot", species: "dog", age: "3", id: 987654321 },
{ name: "Fluffy", species: "rabbit", age: "2", id: 123123123 },
{ name: "Purrsloud", species: "cat", age: "1", id: 456456456 },
{ name: "Paws", species: "dog", age: "6", id: 789789789 },
]);
const pet = pets.map((pet) => (
<Pet name={pet.name} species={pet.species} age={pet.age} id={pet.id} />
));
return (
<div>
<LikedArea />
<TimeArea />
<ul>{pet}</ul>
<AddPetForm set={setPets} />
</div>
);
}
function Pet(props) {
return (
<li>
{props.name}is a {props.species} and is {props.age} years old
</li>
);
}
and this is the child component:
import React, { useState } from "react";
function AddPetForm(props) {
const [name, setName] = useState();
const [species, setSpecies] = useState();
const [age, setAge] = useState();
console.log(props.set);
function handleSubmit(e) {
e.preventDefault();
props.set((prev) => {
prev.concat({ name: name, species: species, age: age, id: Date.now() });
setName("");
setSpecies("");
setAge("");
});
}
return (
<form onSubmit={handleSubmit}>
<fieldset>
<legend>Add New Pet</legend>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={species}
onChange={(e) => setSpecies(e.target.value)}
placeholder="species"
/>
<input
value={age}
onChange={(e) => setAge(e.target.value)}
placeholder="age in years"
/>
<button className="add-pet">Add Pet</button>
</fieldset>
</form>
);
}
export default AddPetForm;
You aren't returning the new concatenated array. So when you call props.set it should be this:
props.set((prev) => {
setName("");
setSpecies("");
setAge("");
return prev.concat({ name: name, species: species, age: age, id: Date.now() });
});
If you don't return anything, then technically the return value is undefined, so that's what it sets the state to. Then you get the error when you try to .map it

How can I load multi select dynamically? [react-select]

How can I load Multi-Select dynamically?
I used react-select to implement MultiSelect.
My Efforts
In componentDidMount(), I fetched an array, which I want to display/load in my multi-select; then a response is stored in state.
Now, I tried to get value from that state, but I didn't get that value.
My Code
state= {Category: []}
// that category contain this values
//0: {categoryid: "1", categoryname: "Select Category"}
//1: {categoryid: "2", categoryname: "Abc"}
componentDidMount() {
fetch("http://myURL//file.php", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(responseJson => {
this.setState({ Category: responseJson });
// If server response message same as Data Matched
console.log(this.state.Category);
window.parent.location = window.parent.location.href;
})
.catch(error => {
console.error(error);
});
}
//this code is not working, display nothing
<Select
closeMenuOnSelect={false}
components={animatedComponents}
isMulti
>
{this.state.Category.map((e, key) => {
return (
<option key={key} value={e.categoryid}>
{e.categoryname}
</option>
);
})}
</Select>
Please help me with this problem
react-select has options props.
<Select
closeMenuOnSelect={false}
components={animatedComponents}
options={this.state.Category.map(e => ({ label: e.categoryname, value: e.categoryid}))}
isMulti
onChange={newValue => this.setState({ selected: newValue })}
/>
How can I select values of this multi-select based on another select
component?
You can store selected values for both selects in state and filter options based on selected value.
I added quick sample with 2 dependent selects - Hospital (can have few doctors) and Doctor (can work in few hospitals).
When you select some Doctor - Hospital selection is updated and vice-versa.
Preview this code
import React, { useState } from "react";
import { render } from "react-dom";
import Select from "react-select";
const data = {
doctors: [
{
id: 1,
name: "Andrew",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 2, title: "Test2" }]
},
{
id: 2,
name: "Another",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 3, title: "Test3" }]
}
],
hospitals: [
{ id: 1, title: "Test Hospital" },
{ id: 2, title: "Test2" },
{ id: 3, title: "Test3" }
]
};
function App() {
const [selectedDoctor, setSelectedDoctor] = useState(null);
const [selectedHospital, setSelectedHospital] = useState(null);
const hospitalOption = item => ({ value: item.id, label: item.title });
const hospitalOptions = () => {
if (selectedDoctor) {
return data.doctors
.filter(doctor => doctor.id === selectedDoctor.value)[0]
.hospitals.map(hospitalOption);
} else {
return data.hospitals.map(hospitalOption);
}
};
const doctorOption = item => ({
value: item.id,
label: `Doctor ${item.name}`
});
const doctorOptions = () => {
if (selectedHospital) {
return data.doctors
.filter(
doctor =>
doctor.hospitals.filter(
hospital => hospital.id === selectedHospital.value
).length
)
.map(doctorOption);
} else {
return data.doctors.map(doctorOption);
}
};
const reset = () => {
setSelectedDoctor(null);
setSelectedHospital(null);
};
return (
<div className="App">
<h3>React-Select multi select sample</h3>
<Select
id="hospital"
value={selectedHospital}
onChange={setSelectedHospital}
options={hospitalOptions()}
selectedDoctor={selectedDoctor}
/>
<Select
id="doctor"
value={selectedDoctor}
options={doctorOptions()}
onChange={setSelectedDoctor}
selectedHospital={selectedHospital}
/>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Doctor: {JSON.stringify(selectedDoctor || {}, null, 2)}
<br />
Available Doctors: {JSON.stringify(doctorOptions() || {}, null, 2)}
</pre>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Hospital: {JSON.stringify(selectedHospital || {}, null, 2)}
<br />
Available Hospitals: {JSON.stringify(hospitalOptions() || {}, null, 2)}
</pre>
<button onClick={reset}>Reset</button>
</div>
);
}
render(<App />, document.getElementById("root"));

Resources