Passing input value to react component - javascript

I'm trying to make a search functionality for my app. It works with API:
http://localhost:3005/products?q=[USER INPUT HERE]
and .JSON is returned from this. I already have a working component that I want to duplicate and use it for search results display. It looks like this:
class Item extends Component {
constructor(props) {
super(props);
this.state = {
output: {},
url: {}
}
}
componentDidMount() {
fetch(this.props.url)
.then(response => response.json())
.then(data => this.setState({ output: data }));
}
render() {
const { general = {name:"", description:""} } = this.state.output;
return (
<BoxTitle>{general.name}</BoxTitle>
);
}
}
working alright, rendered this way:
let ChoosePage = (i) => {
ReactDOM.unmountComponentAtNode(document.getElementById('items'))
let urls = [
'http://localhost:3005/products/774944',
'http://localhost:3005/products/774945',
...
'http://localhost:3005/products/738471'];
let urls_sliced = urls;
if (i === 0) {
urls_sliced = urls.slice(0, 4);
} else if (i === 1) {
urls_sliced = urls.slice(4, 8);
} else if (i === 2) {
urls_sliced = urls.slice(-2);
}
let show_items = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url}/>
)
}), document.getElementById('items'));
}
show_items()}
this is my input field:
const search_box = (
<form>
<Icon>search</Icon>
<input placeholder={'Search...'}></input>
</form>
);
I'm looking for a way to pass value inputted by the user to function that will convert it to link and use for getting .JSON from API and then render components mapped with this data. Managed to make only this:
let url_s = 'http://localhost:3005/products?q=' + input;
let show_results = () => {
ReactDOM.render(urls_sliced.map((url)=>{
return(
<Item url={url_s}/>
)
}), document.getElementById('items'));
}
show_results()
Help is very appreciated here :)

Related

React Drop Down problem with state change

Panel is a datamodel fetched from database. avialablePanels is a dropdown where I can select an option I want. PanelCode dropdown is populated using a lookup table because it acts as a form where the displayed value is what Panel['PanelCode'] has and other values with which I can update. When I update a value of Panel[PanelCode] with the help of indexing using the PanelCode dropdown form it initially updates the value in the Panel['PanelCode'] array. Now lets say I want to update another value in the Panel['PanelCode'] and save them together as soon as I select another option from avialablePanels the first updated value of Panel['PanelCode'] is lost.
Panel: {
PanelCode: [ 1, 4 ]
}
availablePanels:[
{ OptionCode: 'R1-1', OptionKey: 1, OptionValue: 'Stop' },
{ OptionCode: 'R1-3P',OptionKey: 4,OptionValue: 'All Way (plaque)'}
]
export default class PanelTest extends Component {
constructor(props) {
super(props);
console.log(this.props.pointModel)
this.state = {...this.props.pointModel,
availablePanels:[],
selectedIndex: 0,
selectedPanel: null,
tempPanelCode: this.props.pointModel.Panel.PanelCode[0]===null?0:
this.props.pointModel.Panel.PanelCode[0],
}
}
render() {
return(
<Container>
{this.state.availablePanels.length>0 &&
<PtSelect label="Available Panel"
options={this.state.availablePanels}
name="selectedPanel" defaultVal={this.state.selectedPanel}
onChange={this.onChangeSelectedPanelDropdown} />}
{this.renderPanelinfo()}
</Container>
)
}
onChangeSelectedPanelDropdown = (e) => {
const { target } = e;
const {name, value} = target;
let indexVal = this.state.Panel.PanelCode.indexOf(parseInt(value))
this.setState({ [name]: parseInt(value),
selectedIndex:indexVal,
tempPanelCode: this.props.pointModel.Panel.PanelCode[indexVal]===null?0:
this.props.pointModel.Panel.PanelCode[indexVal]
});
}
renderPanelinfo = () =>{
const {typeOptions} = DropdownLib.getSignNum().Signs_Types;
/* typeOptions looks like availablePanels but with more options */
return (
<div>
<PtSelect label="Panel Code" options={typeOptions}
disabled={this.props.disabled}
name="PanelCode" defaultVal={this.state.tempPanelCode}
onChange={this.onChangeDropdown} />
</div>
)
}
getAvaialablePanels=()=>{
const availablePanelOptions = []
const optionKey = []
//const optionvalue = []
fetch(`${config.server}/getavailablepanels/`+this.state.Support.SignId)
.then(response=>
{
return response.json();
})
.then(data=>{
for (var i =0;i<data.length;i++){
availablePanelOptions.push(data[i]['OptionCode'])
optionKey.push(data[i]['OptionKey'])
//optionvalue.push(data[i]['OptionValue'])
}
let dropOptions = availablePanelOptions.map((option,idx)=>{
return {key:optionKey[idx],value: optionKey[idx], label:option}
});
this.setState({
availablePanels:dropOptions
});
})
.catch(error=>{
console.log(error);
});
}
onChangeDropdown = (e) => {
const { target } = e;
const {name, value} = target;
this.props.triggerNeedSave();
// eslint-disable-next-line
let stateVariable = 'temp'+[name]
this.setState({
[stateVariable]: parseInt(value)
});
this.props.pointModel.Panel[name][this.state.selectedIndex] = parseInt(value);
console.log(this.state)
}
componentDidMount(){
this.getAvaialablePanels()
}
}
Any help is really appreciated.

React and jquery-ui/ui/widgets/sortable: Child component not reorder as expect

I have an array in "State" of parent component called "portfolios". You can access by using: "this.state.portfolios".
And a child component called "StockDetailsComponent".
In parent component, I'm using map function to render child component "StockDetailsComponent" like this:
{
this.state.portfolios.map((obj, key) => {
return <StockDetailsComponent key={key} portfolio={obj} onRemovePortfolio={this.onRemovePortfolio}/>;
})
}
It's ok. But when I reorder "this.state.portfolios", child component re-render not as expected.
Before: portfolios = ["object_stock_symbol_1", "object_stock_symbol_2"];
After re-order: portfolios = ["object_stock_symbol_2", "object_stock_symbol_1"];
Parent component looks like as below and lets me explan:
class VndDomain extends React.Component {
constructor(props) {
super(props);
this.socket = null;
this.sortableEnabled = false;
this.state = {
portfolios: []
};
}
render() {
return (
<div id="SQ-vnd">
<HeaderComponent/>
<div className="SQ-body">
<div className="SQ-text-right">
<p className="SQ-d-inline-block SQ-cursor-pointer SQ-mt-5 SQ-mb-5" onClick={this.removeAllPortfolios}>Xóa toàn bộ</p>
</div>
<StockFormCreateComponent onCreatePortfolio={this.onCreatePortfolio}/>
<div id="JSSQ-portfolio">
{
this.state.portfolios.map((obj, key) => {
return <StockDetailsComponent key={key} portfolio={obj} onRemovePortfolio={this.onRemovePortfolio}/>;
})
}
</div>
</div>
<FooterComponent/>
</div>
);
}
componentDidMount() {
this.enableSortable();
this.getAllPortfolioByUserId();
}
/**
* Get all portfolios belong to current user
* #return {Promise} [description]
*/
getAllPortfolioByUserId = async () => {
try {
this.props.dispatch(loadingSpinnerActions.showLoadingSpinner());
let result = await PortfolioService.getAllPortfolioByUserId();
if(result.data.status === "SUCCESSFUL") {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
} else {
throw new Error(`${result.data.message}`);
}
} catch(error) {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
CommonUtilities.ShowLog(error.message);
}
}
/**
* Enable drag and drop to reorder
* #return {[type]} [description]
*/
enableSortable = () => {
let parentEl = $("#JSSQ-portfolio");
// Check duplicate sortable before set
if (this.sortableEnabled) {
parentEl.sortable("destroy");
parentEl.sortable();
} else {
parentEl.sortable();
this.sortableEnabled = true;
}
parentEl.on("sortupdate", async () => {
let sorted = parentEl.sortable("serialize");
let newOrderArrObj = sorted.split("&").map((value) => {
let symbol = value.replace("SQ[]=", "").toUpperCase();
let portfolio = this.state.portfolios.find(obj => obj.symbol === symbol);
return {
_id: portfolio._id,
symbol
};
});
try {
this.props.dispatch(loadingSpinnerActions.showLoadingSpinner());
let result = await PortfolioService.reorderPortfolio({newOrder: newOrderArrObj});
if(result.data.status === "SUCCESSFUL") {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
} else {
throw new Error(`${result.data.message}`);
}
} catch(error) {
this.setState((prevState) => {
return {portfolios: prevState.portfolios};
}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
}
});
}
First, I get list Portfolios from the database, assign to "portfolios" of state, shown in the Client. And enable drag/drop to re-order by "enableSortable" function. At this time, it's working fine.
When I drag to re-order, "this.state.portfolios" changed as expected, I can see in "console.log()" but child component render is wrong. Not as order.
The code is very long, so you only need to pay attention to the following options I tried:
Option 1:
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
result.data.data is data after re-ordered, it's fine but re-render not work as order.
Option 2: If I clear state by an empty array and set it again like code below, it's work because child component has been "Unmount" and re-render instead just update like Option 1.
this.setState({portfolios: []}, () => {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
});
Please help me :( I don't want to setState and then setState again.
It looks like that your portfolio data is a complex object which React is not able to determine that has changed. Yoiu can do like below:
You can do like :
let newPrtfolio = Object.assign ([],result.data.data); // Assign data to new object and assign it
this.setState({portfolios: newPrtfolio }, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
or if your portfolio is an object and not an array than you may try below way:
let newPrtfolio = Object.assign ({},result.data.data); // Assign data to new object and assign it
this.setState({portfolios: newPrtfolio }, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
You may try both the ways, and one of them will work out for you depending upon the structure of your portfolio object

Automatic Columns in React-Table not working with JSON Arrays

I'm trying to dynamically generate a table from a fetch request. It's able to do it with JSON data without an array name, however when it does, it doesn't work. Here is the code: https://codesandbox.io/s/static-example-319q4
Here, the example works fine with the data that doesn't have an array name for the JSON data, however, when the other componentDidMount function is used, it doesn't work even though I specified the array name using "posts.launches".
class App extends React.Component {
constructor(props){
super(props);
this.state = {
posts: [],
value: '',
}
}
/*
Get response from an API endpoint and populates the
*/
componentDidMount() {
//const params = this.state.text
const url = "https://jsonplaceholder.typicode.com/posts";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts });
});
}
/*
componentDidMount() {
//const params = this.state.text
const url = "https://hn.algolia.com/api/v1/search?query=redux";
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(posts => {
this.setState({ posts: posts.hits });
});
}
*/
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
const column =
this.state.posts &&
Object.keys(getPostKeys).map(key => {
return {
Header: key,
accessor: key
};
});
return column;
} else {
console.log("Error")
return [];
}
}
render() {
console.log(this.state.posts[0])
const columns = this.getColumns();
// console.log(JSON.stringify(this.state.initial_data));
return (
<div>
<ReactTable
data={this.state.posts}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
filterable
/>
<br />
</div>
);
}
}
ReactDOM.render( <
App / > ,
document.getElementById('app')
);
Any help would be great! Thanks!
Some of the data in your JSON is not consistent with the input that React Table expects its to be in a grid. Check for the condition in working example -
"_tags" && x !== "_highlightResult"
After removing these keys, I further baked the columns and its working fine. Please check the working example -
https://codesandbox.io/s/static-example-x2kjr
Code -
getColumns() {
const getPostKeys = this.state.posts[0];
if (getPostKeys) {
function isNotTagsOrHighlightKey(x) {
return x !== "_tags" && x !== "_highlightResult";
}
const getSanitizedColumns = Object.keys(getPostKeys).filter(
isNotTagsOrHighlightKey
);
const newColumn = getSanitizedColumns.map(key => {
return {
Header: key,
accessor: key
};
});
return newColumn;
} else {
console.log("Error");
return [];
}
}

how to merge local setState list to Redux list into one list - redux react

Here i have a difficult situation. I have a locationData json in JobsPanel component which is saving location details based on one id(jobId). Now, in my component i have a part 'Configured Location' where i am calling the saved location data and make a setState list ('configuredList') from that json. Now, i have one more part in my application preliminary locations data using redux action calling other api and save into a list 'conLocations'.
Now, i am adding one location item 'conLocation' list (redux state) to 'configuredList'(setState) and updating the changes. It is working fine but last added item showing two times. After trial, i do understand that i have rendered two mapped list. How to merge that into one ? I have done so far this.
configLocation function where i am retrieving last saved location from locationData json.
/** Currently initialize and configure configuredList for retrieving existing job's location data */
configLocation(locationData) {
let configuredList = [];
if (locationData.locations.locationDetails != null && locationData.locations.locationDetails != undefined) {
locationData.locations.locationDetails.map(item => {
let listitem = { ...item };
configuredList.push(listitem);
});
}
this.setState({ configuredList });
}
getLocationData function where i am merging two list that retrieved list and conLocations list and i am calling this function to other component where save changes or update changes operation is happening. It is working fine.
getLocationData() {
let saveableLocationlist = [];
if (this.props.conLocations != null && this.state.configuredList != null) {
const { configuredList } = this.state;
const { conLocations } = this.props;
let totalList = configuredList.concat(conLocations);
saveableLocationlist = totalList;
}
const locationData = {
locationDetails: saveableLocationlist
}
return locationData;
}
here you can see i am updating the locationData json . By calling this function in jobspanel that updated locationData json is now available for my component in 'configLocation' function.
My component code:
export class NewLocationPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
configuredList: [],
chkitems: []
};
this.configLocation = this.configLocation.bind(this);
this.togglePanel = this.togglePanel.bind(this);
this.handleClick = this.handleClick.bind(this);
this.allLocations = this.allLocations.bind(this);
this.clearall = this.clearall.bind(this);
this.getLocationData = this.getLocationData.bind(this);
this.handleRemove = this.handleRemove.bind(this);
this.removeConfigLocation = this.removeConfigLocation.bind(this);
this.removeLocationAll = this.removeLocationAll.bind(this);
this.handleChecklocation = this.handleChecklocation.bind(this);
this.handleCheckedAdded = this.handleCheckedAdded.bind(this);
this.handleCheckedRemove = this.handleCheckedRemove.bind(this);
this.handleActionButton = this.handleActionButton.bind(this);
}
componentDidMount() {
this.props.loadData();
if (this.props.locationData != null && this.props.locationData != undefined) {
this.configLocation(this.props.locationData);
}
}
componentDidUpdate(prevProps, prevState) {
if ((prevProps.jobId != this.props.jobId || prevProps.locationData != this.props.locationData)) {
this.configLocation(this.props.locationData);
}
}
//other codes
/** Currently initialize and configure configuredList for retrieving existing job's location data */
configLocation(locationData) {
let configuredList = [];
if (locationData.locations.locationDetails != null && locationData.locations.locationDetails != undefined) {
locationData.locations.locationDetails.map(item => {
let listitem = { ...item };
configuredList.push(listitem);
});
}
this.setState({ configuredList });
}
/** updating locationData by saving changes - calling this function into jobsPanel */
getLocationData() {
let saveableLocationlist = [];
if (this.props.conLocations != null && this.state.configuredList != null) {
const { configuredList } = this.state;
const { conLocations } = this.props;
let totalList = configuredList.concat(conLocations);
saveableLocationlist = totalList;
}
const locationData = {
locationDetails: saveableLocationlist
}
return locationData;
}
//other codes
render() {
//const{configuredList} = this.state;
const _labels = store.getLabels();
let collapsedToggle = this.props.open ? 'collapsed' : ''
return (
{this.state.open ? (
<div className="panel-body">
<div className="row grid-divider">
<div className="col-sm-6">
<div className="col-padding">
<div className="pos-div"><h4>Configured Location</h4>
<div><table className="table configTableColor"><thead>{this.state.configuredList.map((locc, index) => <tr key={index}><th><input type="checkbox" onClick={() => this.handleCheckedRemove(locc.mruCode)} /><label></label></th><th className="configLocationInfo">{locc.mruCode} - {_labels[locc.division]} - {locc.country}</th><th className="text-right"><img alt="DeleteIcon" onClick={() => { this.removeConfigLocation(index) }} className="deleteIconStyle" src="img/delete_large_active.png" /></th></tr>)}</thead>
<tbody>
{this.props.conLocations.map((loct, index) => <tr key={index}>
<td><input type="checkbox" /><label></label></td>
<td className="configLocationInfo">{loct.mruCode} - {_labels[loct.division]} - {loct.country}</td>
<td className="text-right"><img alt="DeleteIcon" onClick={() => this.handleRemove(loct.mruCode)} className="deleteIconStyle" src="img/delete_large_active.png" /></td>
</tr>
)}
</tbody></table></div>
</div>
</div>
</div>
</div>) : null}
</div>
);
}
}
const mapStateToProps = state => {
return {
location: state.locationRed.location,
conLocations: state.locationRed.conLocations,
isChecked: state.locationRed.isChecked
};
};
const mapDispatchToProps = (dispatch) => {
return {
loadData: () => { dispatch(loadData()) },
addLocation: (mruCode) => { dispatch(addLocation(mruCode)) },
addAllLocation: () => { dispatch(addAllLocation()) },
removeLocation: (mruCode) => { dispatch(removeLocation(mruCode)) },
removeAllLocation: () => { dispatch(removeAllLocation()) },
checkboxState: (mruCode) => { dispatch(checkboxState(mruCode)) },
checkedLocation: () => { dispatch(checkedLocation()) }
}
}
export default connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })(NewLocationPanel);
As you can see i am rendering two list. How to merged into one?
Jobs Panel component where i am initialize and saving locationData details
import React from 'react';
import ReactDOM from 'react-dom';
import LocationPanel from '../panels/NewLocationPanel';
class JobsPanelComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
jobDetailJson: this.props.jobDetailJson
};
this.setLocationPanelRef = cRef =>{this.locationPanel = cRef;};
}
componentWillUnmount() {
this.clearStates();
this.clearRefs();
this.clearBindings();
}
clearStates() {
this.state.jobDetailJson = null;
}
clearRefs(){
this.locationPanel = null;
}
clearBindings(){
this.setLocationPanelRef = null;
}
componentWillMount() {
this.state.jobDetailJson = this.props.jobDetailJson;
}
componentWillReceiveProps(nextProps) {
this.state.jobDetailJson = nextProps.jobDetailJson;
}
saveJobData(jobData){
var locationData = null;
if(some conditions){
locationData = this.locationPanel.getWrappedInstance().getLocationData();
}
//more other lines not related to my mine
}
render(){
var locationDataJson= null;
if(this.state.jobDetailJson != null){
locationDataJson =this.state.jobDetailJson;
}
return(<div className="panel-group" id="jobsPanelGroup">
<LocationPanel ref={this.setLocationPanelRef} locationData ={locationDataJson} jobDetailJson={this.state.jobDetailJson} versionId={versionId} jobName={jobName} jobId={jobId} isForViewOnly={this.props.isForViewOnly} parentJobId={this.props.parentJobId} title="Location"/>
//More coded lines for other things not related to my part
);
}
}
My application flow will be like - Configured Location(initial) configuredList -> conLocations (redux list) -> conLocations(add item) -> Configured Location(intermediate) configuredList + added item(conLocations) -> save changes -> Configured Location(final) - merged List
save changes /update locationData everything is in Jobs Panel but working fine. There is no problem. How to make changes in my component.
The mapStateToProps function is passed both the redux state and the component's props. So you can combine your locations from redux and from props inside mapStateToProps:
// destructuring only the things we need from state (locationRed) and props (locationData)
const mapStateToProps = ({ locationRed }, { locationData }) => ({
location: locationRed.location,
// get a merged set
conLocations: [...locationRed.conLocations, ...(locationData.locations.locationDetails || [])],
isChecked: locationRed.isChecked
})
With this setup you could most likely eliminate your configuredList state and related update functions, your componentDidUpdate function and just render from props.conLocations instead of from state and props in two separate loops.
You could also dedupe locations or do any job id checks you need inside of mapStateProps when merging your lists. If it starts to gets a bit complicated in your mapStateToProps, you could take a look at memoized selectors like reselect that would make that a lot nicer.

React append component programmatically

I want to create a react component instance and render it in a static place programmatically.
My use-case is that I open a sequence of dialogs in an unknown length and when I get a response from a dialog I open the next.
I want to do something like:
const DialogExample = () => ({ question, onAnswer }) =>
(<div>
{question}
<button onClick={onAnswer}>answer</button>
</div>);
class SomeComponent extends React.Component {
async start() {
const questions = await getSomeDynamicQuestions();
this.ask(questions);
}
ask(questions) {
if (questions.length === 0) {
// DONE.. (do something here)
return;
}
const current = questions.pop();
React.magicMethod(
// The component I want to append:
<DialogExample
question={current}
onAnswer={() => this.ask(questions)}
/>,
// Where I want to append it:
document.getElementsByTagName('body')[0]);
}
render() {
return (
<div>
<button onClick={this.start}>start</button>
</div>);
}
}
I know that's not very "react-like", and I guess the "right" way of doing it will be storing those questions in state and iterate over them in "someComponent" (or other) render function, but still, I think that this pattern can make sense in my specific need.
Sounds like a case for Portals. I'd recommend doing something like this:
class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.body = document.getElementsByTagName('body')[0];
this.state = {
questions: [],
}
}
async start() {
const questions = await getSomeDynamicQuestions();
this.setState({ questions });
}
nextQuestion() {
this.setState(oldState => {
const [first, ...rest] = oldState.questions;
return { questions: rest };
})
}
render() {
const { questions } = this.state;
return (
<div>
<button onClick={this.start}>start</button>
{questions.length > 0 && ReactDOM.createPortal(
<DialogExample
question={questions[0]}
onAnswer={() => this.nextQuestion()}
/>,
this.body,
)}
</div>
);
}
}

Categories