function Results(props) {
var results = props.results;
return (
<>
<table>
<thead>
<tr>
<th>Book Name</th>
<th>Author</th>
<th>S.no</th>
<th>Series Name</th>
<th>Type</th>
<th>Genre</th>
<th>Kindle/Real</th>
</tr>
</thead>
<tbody>
{results.map(result => {
return (
<tr key={result.name}>
<td>{result.name}</td>
<td>{result.author}</td>
<td>{result.sno}</td>
<td>{result.series}</td>
<td>{result.type}</td>
<td>{result.genre}</td>
<td>{result.kindeReal}</td>
</tr>
);
})}
</tbody>
</table>
</>
);
}
When I try to render the above com component, I get the error:
TypeError: results.map is not a function
The results variable is an array of objects, something like:
[{"type":1},{"type":0},{"type":2}]
However, when I use the .map function, it returns the error! It is clearly an array, so why can't I use it?
This is the output of console.log(results).
[{"Book_Name":"Time Riders","Author":"Alex Scarrow","S_no":1,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"Day of the Predator ","Author":"Alex Scarrow","S_no":2,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"The Doomsday Code","Author":"Alex Scarrow","S_no":3,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"The Eternal War","Author":"Alex Scarrow","S_no":4,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"Gates of Rome","Author":"Alex Scarrow","S_no":5,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"City of Shadows","Author":"Alex Scarrow","S_no":6,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"The Pirate Kings","Author":"Alex Scarrow","S_no":7,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"The Mayan Prophecy","Author":"Alex Scarrow","S_no":8,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"},{"Book_Name":"The Infinity Cage","Author":"Alex Scarrow","S_no":9,"Series_Name":"Time Riders","Fiction_Non_fiction_Companion_Prequel":"Fiction","Genre":"Sci-fi/Mystery/Thriller","Kindle_Real":"Real"}]
It looks like an array to me. Why is it not an array then?
This is the parent component.
import React from "react";
import Results from "./results";
function ResultsRenderer(props) {
if (props.status === true) {
return <Results results={props.results} />;
} else {
return <>{"No"}</>;
}
}
export default ResultsRenderer;
This is the parent component of ResultsRenderer.
import React, { useState } from "react";
import { useEffect } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import Form from "./searcherFormDumb";
import { toast } from "react-toastify";
import ResultsRenderer from "./resultsRenderer";
function Searcher() {
const [answer, setAnswer] = useState(["Empty", false]);
const [book, setBook] = useState({
name: "",
author: "",
sno: null,
series: "",
type: "",
genre: "",
kindleReal: ""
});
const defaultState = {
name: "",
author: "",
sno: null,
series: "",
type: "",
genre: "",
kindleReal: ""
};
function handleChange(event) {
const updatedBook = { ...book, [event.target.name]: event.target.value };
setBook(updatedBook);
}
function handleSubmit(event) {
event.preventDefault();
var changed = {};
function populateChanged(now, old, title, temp) {
if (now !== old) {
temp[title] = now;
return temp;
} else {
return temp;
}
}
changed = populateChanged(
book.name,
defaultState.name,
"Book_Name",
changed
);
changed = populateChanged(
book.author,
defaultState.author,
"Author",
changed
);
changed = populateChanged(book.sno, defaultState.sno, "S_no", changed);
changed = populateChanged(
book.series,
defaultState.series,
"Series_Name",
changed
);
changed = populateChanged(
book.type,
defaultState.type,
"Fiction_Non_fiction_Companion_Prequel",
changed
);
changed = populateChanged(book.genre, defaultState.genre, "Genre", changed);
changed = populateChanged(
book.kindleReal,
defaultState.kindleReal,
"Kindle_Real",
changed
);
var temp_string = "";
var key = "";
var value = "";
var temp_string_list = [];
//debugger;
for (var i = 0; i < Object.keys(changed).length; i++) {
//debugger;
key = Object.keys(changed)[i];
value = changed[key];
if (i !== Object.keys(changed).length - 1) {
temp_string = `${key} = "${value}" AND `;
} else if (i === Object.keys(changed).length - 1) {
temp_string = `${key} = "${value}"`;
}
temp_string_list.push(temp_string);
//debugger;
temp_string = "";
key = "";
value = "";
}
var sql_t = temp_string_list.join("");
var sql_tt = "SELECT * FROM books_catalouge WHERE ";
var sql = sql_tt + sql_t;
toast.success(sql);
var request = new XMLHttpRequest();
var jsql = JSON.stringify(sql);
request.onreadystatechange = function() {
//debugger;
if (this.readyState == 4 && this.status == 200) {
setAnswer([this.responseText, true]);
console.log(`${answer}`);
}
};
request.open(
"GET",
"http://localhost:3001/retrieve_books" + "?msg=" + jsql,
true
);
request.send(jsql);
console.log("This is the END");
console.log(`${answer}`);
}
return (
<>
<Form book={book} onChange={handleChange} onSubmit={handleSubmit} />
<br />
<ResultsRenderer status={answer[1]} results={answer[0]} />
</>
);
}
export default Searcher;
Let me know if you need the NodeJS as well. I am using SQL to get the data, which is why I need the NodeJS. Sorry if my code is a little weird.
Thanks in advance!
Function map() can be used only on array. In this situation it looks like props.results is not array or has not been set yet (this can happen if you are fetching data with Axios or something like that).
I would recommend you to place something like this at the start of function:
if (!props.results) return 'no data';
if (!Array.isArray(props.results)) return 'results are not array'
after clarification
You get response on your onreadystatechange request which usualy comes in JSON or XML. I think your answer is stringified json. Try to use JSON.parse(response) in case of JSON or (new DOMParser()).parseFromString(response,"text/xml") in case of XML.
In your component it may look like this
request.onreadystatechange = function() {
(this.readyState == 4 && this.status == 200)
&& setAnswer([JSON.parse(this.responseText), true]);
};
I'm seeing similar behavior when deserializing a simple .json file:
const stringifiedData = fs.readFileSync(animalsPath);
const animalData = JSON.parse(stringifiedData);
const animals = animalData.map(animal => {
return new Animal(animal)
})
Oddly, the example I'm following is doing a string dot notation that seems to satisfy V8 that the object is an array, even though it's output in console.out does not change. The following does work but I don't know why. Any ideas?
const stringifiedData = fs.readFileSync(animalsPath);
const animalData = JSON.parse(stringifiedData).animals;
const animals = animalData.map(animal => {
return new Animal(animal)
})
I also got the same error while accessing data element of an array. here is what I did:
{standardObj ? (
{standardObj.map((obj: any) => {
return (
<Grid item>
<FormControlLabel
className={styles.CheckboxLabel}
key={obj.Code}
control={
<Checkbox
onChange={handleCheckElement}
name={obj.FullName}
value={obj.Code}
/>
}
label={obj.FullName}
/>
</Grid>
);
})}
) : null }
I faced the same problem and appars that for the useState Hook is very important the type of data of the inital value, I had as initial value a 0 int and cos that react didn't recognize the update as array.
I am new to React and found out that if a variable is empty / undefined and used with "map" function, the React throws that exception. My solution was to check a variable for "undefined" and length > 0.
Most of the time this problem is because of wrong syntax.
where there is
results.map(result => {
........................
}
just do
results.map(result => (
........................
)
First of all one general question that how to concat setState array with redux array? In my implementation, there is a configured list which is based on setState array of objects. Now, i have a location list which is based on redux array of objects. Now i am adding one item from location list to configured list using concat and saving the data. It is successfully saved but in merged list it is displaying 3 items (that redux array item 2 times).
But, i checked in console although it is showing correct result(2 items) but why it is displaying 3 items(redux item 2 times).
Component code:(getLocationData method where i did the concatenation part and calling that method in other component using callback ref and working fine)
export class NewLocationPanel extends React.Component{
constructor(props){
super(props);
this.state={
open:false,
configuredList:[],
retrievedList:[]
};
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.mergedLocation = this.mergedLocation.bind(this);
}
togglePanel (e){
this.setState({open : !this.state.open});
}
handleRemove(mruCode){
this.props.removeLocation(mruCode)
}
handleClick (mruCode){
this.props.addLocation(mruCode)
}
allLocations (){
this.props.addAllLocation()
}
clearall (){
this.props.removeAllLocation()
}
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.props.locationData != null && this.props.locationData != undefined) {
this.configLocation(this.props.locationData);
this.mergedLocation();
}
}
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},()=>{
console.log(this.state.configuredList);
});
}
removeConfigLocation(index){
this.setState({
configuredList:this.props.locationData.locations.locationDetails.filter((_,i)=>i!==index)
},()=>{
console.log(this.state.configuredList);
});
}
mergedLocation(){
if(this.props.conLocations != null && this.state.configuredList !=null){
const{configuredList} = this.state;
let retrievedList = configuredList;
this.props.conLocations.forEach(loct => {
const locationAdded = retrievedList.find(_loct=>loct.mruCode=== loct.mruCode)
});
this.setState({
retrievedList},()=>{
console.log(this.state.retrievedList);
});
}
}
getLocationData(){
let saveableLocationlist = [];
if(this.state.retrievedList != null){
saveableLocationlist = retrievedList;
}
const locationData = {
locationDetails : saveableLocationlist
}
return locationData;
}
render(){
//const{configuredList} = this.state;
const _labels = store.getLabels();
let collapsedToggle = this.props.open ? 'collapsed' : ''
return(
<div className="panel panel-default">
<div className="panel-heading" onClick={(e)=>this.togglePanel(e)}>
<div className="row">
<div className="col-xs-12 col-sm-8 col-md-6 col-lg-6 panelHeadingLabel">
<span>{this.props.title}</span>
</div>
<div className="pull-right">
<span className="defaultHeaderTextColor">{this.state.configuredList.map((loc,index)=><span key={index}>{loc.mruCode} - {_labels[loc.division]} - {loc.country}</span>)}
<span onClick={(e)=>this.togglePanel(e)} className={this.state.open ? "collapse-chevronn" : "collapse-chevron"} aria-hidden="true"></span>
</span>
</div>
</div>
</div>
{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"><h3>Locations List</h3><button style={{ display: this.props.location.length === this.props.conLocations.length ? "none" : "block" }} className="allLargeBtn" onClick={()=>{this.allLocations()}}>Add all locations</button></div><hr/>
{this.props.location.map((item,index)=>(
<div key={index}><div><b>{item.mruCode} - {_labels[item.division]} - {item.country}</b>{!this.props.conLocations.find(item2 => item.mruCode === item2.mruCode)&&(<div className="pull-right jd"><button style={{ display: this.state.configuredList.find(item3=> item.mruCode===item3.mruCode) ? "none" : "block" }} className="call-to-action" onClick={()=>{this.handleClick(item.mruCode)}}>Add Location</button></div>)}<hr/></div></div>))}
</div>
</div>
<div className="col-sm-6">
<div className="col-padding">
<div className="pos-div"><h3>Configured Location</h3><button className="allLargeBtn" onClick={()=>this.clearall()}>Remove all location</button></div><hr/>
<div><table className="table"><tbody>{this.state.retrievedList.map((locc,index)=><tr key={index}><td><b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b></td><td className="text-right"><img alt="DeleteIcon" onClick={()=>{this.removeConfigLocation(index)}} 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
};
};
const mapDispatchToProps = (dispatch) => {
return{
loadData:()=>{dispatch(loadData())},
addLocation:(mruCode)=>{dispatch(addLocation(mruCode))},
addAllLocation:() =>{dispatch(addAllLocation())},
removeLocation: (mruCode)=>{dispatch(removeLocation(mruCode))},
removeAllLocation: () =>{dispatch(removeAllLocation())}
}
}
export default connect(mapStateToProps,mapDispatchToProps,null,{withRef:true})(NewLocationPanel);
Jobs Component(where i am calling getLocationData to save updated value of jobs...It is saving the details properly)
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
);
}
}
I am adding current output which is showing 3 results but it should show 2 results. How to prevent that.Please help me on this.
UPDATE #1:
mergeLocationData(){
let mergedList = [];
// you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`.
if(this.props.conLocations !== undefined && this.state.configuredList !== null){
const { configuredList } = this.state;
const { conLocations } = this.props;
mergedList = configuredList;
this.props.conLocations.forEach(location => {
const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
if(!locationAdded){
mergedList.push(location)
}
});
}
// instead of setting state, return the mergedList
return mergedList; //[ always an array of elements ]
}
And then, at the render method: instead of getting the list from the state, we just invoke the function.
<thead>
{
this.mergeLocationData().map((locc,index)=> (
<tr key={index}>
<th>
<b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
</th>
<th className="text-right">
<img
alt="DeleteIcon"
onClick={()=>{this.removeConfigLocation(index)}}
className="deleteIconStyle"
src="img/delete_large_active.png" />
</th>
</tr>
)
}
</thead>
There is nothing wrong with the code, I suppose it's correctly working, but, it's the logic that is not right, you are rendering both of the arrays in your render, you are not using the function getLocationData anyware in your component, eventhough, concat won't solve the problem.
You can do the following steps to fix the logic.
- Fix the logic of getLocationData:
mergeLocationData(){
// you either have to setup initial state for conLocations in your reducer to `undefined` or to `null`.
if(this.props.conLocations !== undefined && this.state.configuredList !== null){
const { configuredList } = this.state;
const { conLocations } = this.props;
let mergedList = configuredList;
this.props.conLocations.forEach(location => {
const locationAdded = mergedList.find(_location => _location.mruCode === location.mruCode);
});
this.setState({
mergedList
});
}
}
Use the function as a callback to your fetch request, supposibly in componentDidMount after loading the data correctly.
Render the new Array of mergedList which should be in your state by now, and don't forget to add mergedList: [] to your state.
Basically you need to replace the two maps that render location elements in your render with that.
<thead>
{
this.state.mergedList.map((locc,index)=> (
<tr key={index}>
<th>
<b>{locc.mruCode} - {_labels[locc.division]} - {locc.country}</b>
</th>
<th className="text-right">
<img
alt="DeleteIcon"
onClick={()=>{this.removeConfigLocation(index)}}
className="deleteIconStyle"
src="img/delete_large_active.png" />
</th>
</tr>
)
}
</thead>
I haven't seen a post to answer this question yet, so forgive me if I overlooked something. I'm trying to populate a table with ONLY the nested array from my data on the page. this.state.data SHOULD contain a subCollection of "masterTicketItems" from the "master ticket" that is being into this React JSX script from the Razor View.
The trouble comes when I'm trying to optimistically update a given table, and ONLY focusing on that, rather than actively posting data. I've been trying look around to understand this ReactJS and ES6 "spread" mapping technique, and I'm not sure if that applies here or something different.
In essence, I'm trying to ONLY map a "LogList" component with a collection contained INSIDE "this.state.data" inside the parent "Ticket" component. I currently have if/else logic to detect if there is nothing in the collection yet, and if not, try to populate it with a SINGLE object from the child component that was "posted" from the LogForm component. However, I can't seem to get the page to render with the single child in the table! Here's the Ticket JSX file snippet for review:
class Ticket extends React.Component {
constructor(props) {
super(props);
this.state = { data: this.props.initialData };
this.handleLogSubmit = this.handleLogSubmit.bind(this);
}
loadLogsFromServer() {
const xhr = new xmlhttprequest();
xhr.open('get', this.props.geturl + this.props.id, true);
xhr.onload = () => {
const data = json.parse(xhr.responsetext);
this.setState({ data: data });
};
xhr.send();
}
handleLogSubmit(log) {
const logs = this.state.data.apptConfirmItems;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// use a more robust system for ID generation.
if (!logs) {
log.ticketItemId = 1;
const newLogs = log;
let masterItemsAccess = Object.assign({}, this.state.data);
masterItemsAccess.masterTicketItems = log;
this.setState((data) => Object.assign({}, data, { masterTicketItems: [log] }));
}
else {
log.ticketItemId = logs.length + 1;
const newLogs = logs.concat([log]);
this.setState({ data: newLogs });
}
const data = new FormData();
data.append('ItemType', log.itemType);
data.append('ItemDescription', log.text);
const xhr = new XMLHttpRequest();
//xhr.open('post', this.props.submitUrl, true);
//xhr.onload = () => this.loadLogsFromServer();
//xhr.send(data);
}
componentDidMount() {
window.setInterval(() => this.loadLogsFromServer(), this.props.pollInterval);
}
render() {
if (!this.state.data.masterTicketItems || this.state.data.masterTicketItems.Count == 0) {
return (
<div className="queue">
<h1>Affirm Logs</h1>
<h2>{this.state.data.summary}</h2>
<h5><i>{this.state.data.description}</i></h5>
<div><h3>No logs at this time!</h3></div>
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
else {
return (
<div className="queue">
<h1>Affirm Logs</h1>
<h2>{this.state.data.summary}</h2>
<h5><i>{this.state.data.description}</i></h5>
<LogList data={this.state.data.masterTicketItems}/>
<LogForm onLogSubmit={this.handleLogSubmit} />
</div>
);
}
}
}
class LogList extends React.Component {
render() {
const logNodes = this.props.data.masterTicketItems.map(log => (
<Log key={log.ticketItemId}>
<td>{log.itemType}</td> < td > {log.itemDescription}</td>
</Log>
));
const logRaw = this.props.data.masterTicketItmes.map(log => (
<tr>
<td>{log.itemType}</td><td>{log.itemDescription}</td>
</tr>
));
return (
<div className="logList">
<table id="affirmTable" className="table table-bordered table-hover table-striped">
<tbody>
{logNodes}
</tbody>
</table>
</div>
);
}
}
class LogForm extends React.Component {
constructor(props) {
super(props);
this.state = { itemType: 'Test Log', itemDescription: '' };
this.handleItemTypeChange = this.handleItemTypeChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleItemTypeChange(e) {
this.setState({ itemType: e.target.value });
}
handleTextChange(e) {
this.setState({ itemDescription: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
const itemType = this.state.itemType.trim();
const itemDescription = this.state.itemDescription.trim();
if (!itemType || !itemDescription) {
return;
}
this.props.onLogSubmit({ ItemType: itemType, ItemDescription: itemDescription });
this.setState({ itemType: '', text: '' });
}
render() {
return (
<form className="logForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.itemType}
onChange={this.handleItemTypeChange}
/>
<input
type="text"
placeholder="Enter working log here..."
value={this.state.itemDescription}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
}
I purposely left out the child Log component, since I'm confident that when this is addressed that component WILL populate with the table data as expected.
Should I use nested mapping with ES6? Is there a simpler way? I'm trying to understand React state logic a little bit better.
I am following a tutorial and cannot get the following code to run.
When I run the following code I get the error Can't add property attachToForm, object is not extensible. Are you no longer to allowed to change child props in this way (ie. with child.props.key = value)? If not can you see a better way of adding a function to nested children only if the element is an input?
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
I am using es6 if that changes anything but the tutorial can be found here: http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html
FormComponent:
'use strict';
import React from 'react';
import BaseComponent from '#client/base-component';
export default class Form extends BaseComponent {
constructor(props) {
super(props);
this.state = {};
}
render() {
var classString = this.props.className ? this.props.className : '';
var classArray = ['_common-form', this.props.type ? 'form--' + this.props.type : 'form--basic' ];
for(var i = 0; i < classArray.length; i++){
if(classArray[i]){
classString = classString + ' ' +classArray[i];
}
}
var props = {
type: this.props.type,
className: classString,
style: this.props.style,
onSubmit: this.props.onSubmit
};
return React.createElement(
'form',
props,
this.newChildren
);
}
componentWillMount() {
this.inputs = {};
this.model = {};
this.newChildren = [];
this.registerInputs(this.props.children);
}
registerInputs(children) {
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
}
attachToForm(component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state.value;
}
detachFromForm(component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
}
}
ModalComponent:
'use strict';
import React from 'react';
import Modal from '#client/common/modal';
import Button from '#client/common/buttons';
import AddParticipant from './add_participant';
import Form from '#client/common/form_elements';
import Input from '#client/common/form_elements/input.js';
import SlideToggle from '#client/common/form_elements/slide_toggle.js';
import FormField from '#client/common/form_elements/form_field.js';
import FormSection from '#client/common/form_elements/section.js';
import BaseComponent from '#client/base-component';
export default class EditTeam extends BaseComponent {
constructor(props) {
super(props);
this.state = {
values: {
name: props.team.name,
mission: props.team.mission,
globalSearch: props.team.globalSearch,
public: props.team.public,
witcryptSecured: props.team.witcryptSecured
},
addParticipantModal: false
};
}
render() {
var participantsList = [];
this.props.team.participants.forEach(function(participant) {
participantsList.push(
<div className="participant" key={participant.key}>
<span className="participant-avatar" style={{backgroundImage:`url("${participant.avatar}")`}}></span>
<span>{participant.name}</span>
<span className={`${participant.roll}-action roll`}>{participant.roll}<a></a></span>
</div>
);
}.bind(this));
return (
<Modal className="_common-edit-team-settings" title={`Edit ${this.props.team.name}`} isOpen={this.props.modalIsOpen && this.props.editTeamModal} onCancel={this.props.toggleEditTeamModal} backdropClosesModal>
<Form onSubmit={this.saveChanges}>
<FormSection className="edit-team-details" sectionHeader="Team Details">
<FormField label="Name">
<Input name="name" value={this.state.values.name} onChange={this.handleInputChange} type="text" placeholder={this.props.team.name}/>
</FormField>
<FormField label="Mission">
<Input name="mission" value={this.state.values.mission} onChange={this.handleInputChange} type="text" placeholder={this.props.team.kitMission || 'Kit Mission'} multiline />
</FormField>
</FormSection>
<FormSection className="privacy-settings" sectionHeader="Privacy Settings">
<FormField label="Included in global search results" >
<SlideToggle name="globalSearch" defaultChecked={this.state.values.globalSearch} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Accessible by anyone" >
<SlideToggle name="public" defaultChecked={this.state.values.public} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Secured with WitCrypt" >
<SlideToggle name="witcryptSecured" defaultChecked={this.state.values.witcryptSecured} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
</FormSection>
<FormSection sectionHeader="Participants">
{participantsList}
<div id="add-participant" className="participant" onClick={this.toggleAddParticipantModal}>
<span className="participant-avatar" style={{backgroundImage:'url(/img/blue_add.svg)'}}></span>
<span>Add a Participant</span>
<span className="add-action roll"><a></a></span>
</div>
</FormSection>
<Button type="hollow-primary" size="md" className="single-modal-btn" block submit>Save</Button>
</Form>
<AddParticipant people={this.props.people} toggleAddParticipantModal={this.props.toggleAddParticipantModal} modalIsOpen={this.props.modalIsOpen} toggleAddParticipantModal={this.toggleAddParticipantModal} addParticipantModal={this.state.addParticipantModal} />
</Modal>
);
}
toggleAddParticipantModal() {
this.setState({
addParticipantModal: !this.state.addParticipantModal
});
}
handleCheckedChange(event){
var newState = this.state;
newState.values[event.target.name] = !newState.values[event.target.name];
this.setState(
newState
);
}
handleInputChange(event){
var newState = this.state;
newState.values[event.target.name] = event.target.value;
this.setState(
newState
);
}
saveChanges(e){
e.preventDefault();
}
}
Are you using React v.14 or above? the props object is now frozen and cant be changed. You can use React.cloneElement instead
I'm using CKEditor with my project and im trying to prefill the textarea with data im getting from the server, that way it is easier for a user to edit. I do so in this component:
import React from 'react';
import {Link,State, Route} from 'react-router';
import Router from 'react-router';
import ReportsActions from 'actions/ReportsActions';
import ReportsStore from 'stores/ReportsStore';
import $ from 'jquery';
export default class EditReport extends React.Component {
constructor(props) {
super(props);
this.state = ReportsStore.getState();
this.state.singleReport = [];
this.state.editReport = [];
this.state.link = window.location.href;
}
componentDidMount() {
let state = this.state.link;
state = state.split('/');
state = state[state.length-2];
ReportsActions.getSoloReport(state);
ReportsStore.listen(this._onChanges);
CKEDITOR.replace( 'ckedit', {
allowedContent : true,
pasteFromWordRemoveFontStyles : false,
pasteFromWordRemoveStyles : false
});
console.log(this.state.editReport);
}
componentWillUnmount() {
ReportsStore.unlisten(this._onChanges);
}
_onChanges = () => {
this.setState({
singleReport: ReportsStore.getState().singleReport,
duplicate: ReportsStore.getState().singleReport,
editReport: ReportsStore.getState().editReport
});
}
_onCreateReport = () => {
for ( var instance in CKEDITOR.instances )
CKEDITOR.instances[instance].updateElement();
let state = this.state.link;
state = state.split('/');
state = state[state.length-2];
const title = React.findDOMNode(this.refs.title).value;
const date = React.findDOMNode(this.refs.date).value;
const body = React.findDOMNode(this.refs.body).value;
const id = state;
ReportsActions.editReport({
title: title,
date: date,
body: body,
id: id
});
}
render() {
let singleReport = this.state.singleReport;
let editReport = this.state.editReport;
let ckbody = "fetching data..";
if(ckbody == undefined) {
ckbody = "Fetching data..";
}
else {
ckbody = editReport.body;
}
let state = this.state.link;
if(CKEDITOR.instances['ckedit'] == undefined) {
console.log('not defined');
}
else {
console.log(CKEDITOR.instances['ckedit'].getData());
CKEDITOR.instances['ckedit'].setData(ckbody);
}
state = state.split('/');
state = state[state.length-2];
return(
<div>
<main>
<h1>Hello im trying to edit</h1>
<fieldset className = "fieldSet2">
<input type = "text" placeholder = {editReport.title} ref = "title"/>
<input type = "text" placeholder = {editReport.date} ref = "date"/>
<textarea className = "ckeditor" id = "ckedit" ref = "body" name = "ckedit" defaultValue = {editReport.body}>{editReport.body}</textarea>
<Link to= "singlereports" params ={{id: state}}><button type="submit" rows = "5" cols = "5" className ="superButton" onClick={this._onCreateReport}>Edit Report</button></Link>
</fieldset>
<div className ="toMyEmployees2">
<div className="containers1">
<div className="spacer">
<Link to="AllEmployees">
<a className="wide blue">
<i className="fa fa-users"></i>
<h2>View All Employees</h2>
</a>
</Link>
</div>
</div>
<div className ="toMyEmployees3">
<div className="containers1">
<div className="spacer">
<Link to="dashboard">
<a className="wide redgay">
<i className="fa fa-tachometer"></i>
<h2>Dashboard</h2>
</a>
</Link>
</div>
</div>
</div>
</div>
</main>
</div>
);
}
}
I've set CKEditor and initialized it in ComponentDidMount, then in render I try to setHTML to one of my states. When I first visit the component the editor displays the data inside it that I wan't to edit, however if I hit the back button or any other link/route and return to the editor, it spits out 'cannot read property of getSelection of undefined', tracing it, it belongs to this.document.getWindow. This is a single page app as well. I don't know why it's doing this, but maybe I have to remove the instance as well?