I’ve created InitializePhoneNumbersPanel:
class InitializePhoneNumbersPanel extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(phoneNumbers) {
const { dispatch, operatorId } = this.props;
dispatch(updateOperatorData(operatorId, phoneNumbers, {include: 'phone_numbers'}));
}
render() {
const {
handleSubmit,
submitting,
fields: { phone_numbers }
} = this.props;
console.log('\n... Render ...');
console.log('phone_numbers <<<<< ', phone_numbers);
if (_.isEmpty(phone_numbers)) {
return (
<div className={"fade in"}>
Hello
</div>
)
}
return (
<form onSubmit={handleSubmit(this.onSubmit)}>
<div className="row">
<div className="col-md-12">
<ul className="list-unstyled m-b-0 clearfix">
{phone_numbers && phone_numbers.map((phone, index) =>
<PhoneNumbersPanelItem key={index} phone={phone} phone_numbers={phone_numbers}
index={index}/>
)}
</ul>
</div>
</div>
<div className="row">
<div className="col-md-12">
<button type="button" className="btn btn-sm btn-success" onClick={event => {
event.preventDefault();
phone_numbers.addField();
}}><i className="fa fa-plus"></i>
</button>
</div>
</div>
<hr/>
<div className="row">
<div className="col-md-12">
<button type="submit" disabled={ submitting } className="btn btn-sm btn-success pull-right">
Save
</button>
</div>
</div>
</form>
)
}
}
Then this component is wrapped by Redux-form:
InitializePhoneNumbersPanel = reduxForm({
form: 'phone-numbers-panel',
fields
})(InitializePhoneNumbersPanel);
Then everything is wrapped by connect method to make data from Store accessible in Redux-form as fields:
function select(state) {
return {
initialValues: {
phone_numbers: _.map(state.operators.items[state.operators.selectedOperator] && state.operators.items[state.operators.selectedOperator].phone_numbers, phoneId => {
return state.phoneNumbers.items[phoneId];
})
},
operatorId: state.operators.selectedOperator
};
}
InitializePhoneNumbersPanel = connect(select)(InitializePhoneNumbersPanel);
The error is…
The code above works normally however in PhoneNumbersPanelItem component phone numbers which come from “phone_numbers” variable are repeated.
When the operators page(whose phone numbers are shown using PhoneNumbersPanelItem) is loaded the first time no errors occur, however if I choose other operator, Route will change which means operatorId param in store will change which means operators object will change and the phone numbers will be different… changed data are sent to component here:
function select(state) {
return {
initialValues: {
phone_numbers: _.map(state.operators.items[state.operators.selectedOperator] && state.operators.items[state.operators.selectedOperator].phone_numbers, phoneId => {
return state.phoneNumbers.items[phoneId];
})
},
operatorId: state.operators.selectedOperator
};
}Operator
};
}
InitializePhoneNumbersPanel = connect(select)(InitializePhoneNumbersPanel);
So if the number of phone numbers of chosen operator is less than the previous one had , the error is thrown
Uncaught Invariant Violation: findComponentRoot(...,
.0.0.0.1.2.0.0.2.1.0.1.1.0.0.0.$1.0.0.0.0.1.1.0): Unable to find
element. This probably means the DOM was unexpectedly mutated (e.g.,
by the browser), usually due to forgetting a when using
tables, nesting tags like , , or , or using non-SVG
elements in an parent. Try inspecting the child nodes of the
element with React ID ``.
As I understood, the error is thrown because at the beginning there were 3 phone numbers for example, and when I choose a new operator the number of phones is 2 and React seemingly fails to find html code for the third number as in the new rendering this element was not created
Even though there is an error, everything works ok. Probably with another rendering react understands that the state has updated and rerenders virtual DOM
If all operators have the same number of phone numbers, NO error occur AT ALL
How can I fix this error? Has anybody encountered anything like that? So strange that React doesn’t understand that the virtual DOM has changed when we switch to a new Route.
I’ll appreciate any help/solution to this problem
I've tried multiple things to make it work on mine. I had a similar problem.
Apparently the problem was with the type of the button. ReactDOM gets lost if you use a type="button"
I removed the type="button" and added a event.preventDefault() on my onClick handler and it worked for me.
I had a very similar scenario. I tried a bunch of things and the only thing that worked for me was updating react and react-dom to version 15.3.2 (from 0.14.2).
Related
import React from 'react'
import CoinItem from './CoinItem'
const Coins = (props) => {
return (
<div className='container'>
<div>
<div className='heading'>
<p>#</p>
<p className='coin-name'>Coins</p>
<p>Price</p>
<p>24h</p>
<p className = 'hide-mobile'>Volume</p>
<p className = 'hide-mobile'>Market Cap</p>
</div>
{props.coins.map(coins => {
return (
<CoinItem coins={coins} key={coins.id} />
)
})}
</div>
</div>
)
}
export default Coins
With this code, my app won't render and I get the error, "Uncaught TypeError: props.coins.map is not a function." However, it directs me to the line where I create a div with the class name of the heading.
However, when I comment out:
{props.coins.map(coins => {
return (
<CoinItem coins={coins} key={coins.id} />
)
})}
and uncomment it again, my app renders perfectly with the API working, but once again, when I refresh, the app de-renders.
How do I fix this?
CodeSandbox link: https://codesandbox.io/s/busy-cloud-pcfne0?file=/src/components/Coins.js:409-512
A few things you can do is:
Ensure that the "coins" prop exists.
If it exists, make sure it is an Array and not an Object. You cannot use .map() on an Object. it is an Array method.
To avoid the code running a .map() when coins is null/undefined, add a ? after props.coins so that the .map() runs only if coins is defined.
{props.coins?.map(coin => { /*YOUR CODE HERE*/ })
I have the following component which transforms checkbox into nice toggle switch
const ToggleSwitch = forwardRef((props, ref) => {
return (
<div className={`toggle btn btn-lg>
<input type='checkbox' onChange={handleOnChange} ref={ref} />
<div className='toggle-group'>
<label htmlFor={`toggle-${uniqueId}`} className={`btn btn-success btn-lg toggle-on`} ref={labelOn}>In Use</label>
<label htmlFor={`toggle-${uniqueId}`} className={`btn btn-danger btn-lg toggle-off`} ref={labelOff}>Not in Use</label>
<span className={`toggle-handle btn btn-light btn-lg`} ref={handle}></span>
</div>
</div>
);
});
Iam having an issue understanding how forward ref works and iam getting some errors. It is possible as well that iam confused by HOC components too
If i called this from different component like this:
<ToggleSwitch checked={false} toggleName='monitoring' ref={(e) => {monitoring.current = e;register(e);}}/>
it works as expected without any issues. However if i call it as per below:
I have a table component which get data from body:
const body = [{ key: 'inUse', type: 'component', component: ToggleSwitch, props: { checked: 'inUse', onChange: onToggleChange } }, { key: 'vlan' }, { key: 'clientId' }];
Each key is column. Now in table component i have a switch based on type.
If type = "component"
return <td>{component({ ...field.props, checked: entry[field.props.checked], entry: entry })}</td>
In here i get an error:
Uncaught TypeError: component is not a function
Any help would be appreciated. I have looked through similar questions but couldnt find any solution. Thank you
You should not invoke your components directly, but pass them into createElement function:
return (
<td>
{createElement(component, {
...field.props,
checked: entry[field.props.checked],
entry: entry,
})}
</td>
);
You can also use JSX here, but with JSX you will have to name your component property with capital letter, so transformation would not confuse it with html or custom element.
I'm building an open forum and I got everything working the way I want however, I recently ran into a small bug in my code. While a lot of forums require a log in to view topics that are asked, mine is built without the user having to register or log in (I built it like this in case the user simply wants to view topics without having to register or log in). For one of the functionalities, I have it to where users can delete there replies for a specific question thread.
Therefore, I got it to where it only recognizes the users ID and it'll delete the reply based off of that. However, if the user is not logged in, they'll still be able to delete the reply (this is where the bug is).
So my question is it possible that I can check for 2 conditions inside my ternary operator?
I want to check 1st if the user is logged in, if they are, check if the userId matches the reply_user_id and if both cases pass, they'll be able to delete there reply. However, if one fails Don't display the trash icon. Right now, the trash icon works fine when logged in but it's displaying the trash icon if the user is not logged in.
I have a lot of code so I'm only going to show the portion relating to my question:
import React from 'react';
import Header from '../common/Header';
import { Link } from 'react-router-dom';
import { Modal, Button } from 'react-bootstrap';
import Pagination from "react-js-pagination";
import ForumpageService from '../../services/forumService';
import appController from '../../controllers/appController';
import { confirmAlert } from 'react-confirm-alert';
class Forumreplies extends React.Component {
constructor(props){
super(props);
this.deleteReply = this.deleteReply.bind(this);
this.pagination = this.pagination.bind(this);
this.state = {
topicId: 0,
replyId: 0,
userId: this.props.match.params.userid,
postDetails: {},
repliesData: [],
reply: '',
errorMsg: '',
isLoggedin: false,
canDelete: false,
}
}
async componentDidMount(){
// Check if user is logged in
if(localStorage.getItem('userData') !== null) {
this.setState({isLoggedin: true})
}
const topicId = this.props.match.params.topicid
const postDetails = await ForumpageService.replyDetails({topicId: topicId})
this.setState({
postDetails: postDetails[0],
topicId: topicId
})
await this.postReplies();
console.log(this.state);
}
}
deleteReply(id, e){
confirmAlert({
customUI: ({ onClose }) => {
return (
<div className='custom-ui'>
<h1>Are you sure</h1>
<p>You want to delete this reply?</p>
<button className="btn btn-primary" onClick={onClose}>Cancel</button>
<button className="btn btn-primary" onClick={() => {this.confirmDelete(id); onClose();}}>Confirm</button>
</div>
)
}
})
}
render(){
const repliesData = currentReply.map((row, index) => {
return (
<div className="reply-container" key={index}>
{row.reply_status == 0 ?
<div className="row" id="reply-messages-deleted">
<div className="col-md-8">
<p>{row.userName}</p>
<p>{row.reply_message}</p>
<p>This reply has been deleted</p>
</div>
<div className="col-md-2">
// Multiple condition I want to check, right now it's only checking 1 condition which is the userid to the reply id but I want to also check if the user is offline as well.
{this.state.userId == row.reply_user_id ? <i className="far fa-trash-alt" onClick={this.deleteReply.bind(this, row.reply_id)} title="Delete this reply?"></i> : null }
</div>
</div>
:
<div className="row" id="reply-messages" key={index}>
<div className="col-md-8">
<p>{row.userName}</p>
<p>{row.reply_message}</p>
</div>
<div className="col-md-2">
{this.state.userId == row.reply_user_id ? <i className="far fa-trash-alt" onClick={this.deleteReply.bind(this, row.reply_id)} title="Delete this reply?"></i> : null }
</div>
</div>
}
</div>
)
})
export default Forumreplies;
Having some trouble trying to focus in on an element. I have a mapped array function that spits out html with inputs. It is possible to have multiple id's, so I want to set the ref to be 'type' + Id. The two possible types are task and subtask. When I try access via this.refs.{refValue}.focus() I get a Cannot read property 'focus' of undefined
Here's my jsx:
<input className="ui-input-text" type="text" ref="subTask + {subTask.Id}" onChange={this.handleSubTaskChange.bind(this, indx, idx)} value={subTask.Name} />
Here's where I get my error
var subTaskRef = 'subTask' + subTaskId;
this.refs.subTaskRef.focus();
The variable subTaskId is correct, I have verified that. Perhaps I am setting the ref incorrectly?
EDIT
After following #Ori Drori's answer, here's some more code:
class Tasks extends React.Component {
focusTasks: [],
focusSubTasks: [],
constructor(props) {
super(props);
this.state = {
editableTasks: [],
editableSubTasks: [],
tasks: [],
subTasks: [],
plannerId: this.props.plannerId,
};
var state = this.state;
}
and (part) of my render method
render() {
const tasks = this.state.tasks.map((task, idx) => {
var editable = this.state.editableTasks.filter(id => id === task.Id).length > 0;
var editableSubTasks = this.state.editableSubTasks;
const subTaskComponents = task.SubTasks.map((subTask, indx) =>
<li key={subTask.Id} className="list-group-item" style={{minHeight: '50px', border: 0, backgroundColor: 'rgba(127,191,63,.42)'}}>
<div className="pull-left" style={{width: '50%'}}>
<!-- Pay attention to this line -->{editableSubTasks.filter(id => id === subTask.Id).length > 0 ? <input className="ui-input-text" type="text" ref={ (ref) => this.focusSubTasks[subTask.Id] = ref } onChange={this.handleSubTaskChange.bind(this, indx, idx)} value={subTask.Name} /> : <span>{subTask.Name}</span>}
</div>
<div className="pull-right" style={{marginTop: '-5px', width: '50%'}}>
<div className="pull-right">
<button className="btn btn-default" onClick={() => { this.EditSubTask(task.Id, subTask.Id)}}>{editableSubTasks.filter(id => id === subTask.Id).length > 0 ? <i className="fa fa-check"></i> : <i className="fa fa-pencil-square-o"></i>}</button>
</div>
</div>
</li>
);
Here's where the issue seems to be (won't build)
Ended up just using jQuery, it's much easier when it's one line of code. Not sure if what I'm doing is too complicated for this, but I ended up setting an id on the inputs, and just calling $(el).focus() to solve this problem. Unless someone has a working example, I will update this SO.
Using the ref callback just to set a property on the class is a common pattern for accessing DOM elements and React creators recommend to use this pattern instead of this.refs.myRef pattern.
class MyComponent extends React.Component {
// ..
render() {
return (
<input ref={(thisEl) => { this['name' /* + subTask.Id */] = thisEl }} />
)
}
}
Now you can just use it as this['name' /* + subTask.Id */].focus().
However, Im not 100% sure if that could be the cause of your issue, especially because you didn't let us know if console.log(this.refs) actually has correct elements and if you didn't make mistakes.
Let me know how it works out for you.
I don't recommend to use jQuery, in other words: don't mix avoidable imperative code with declarative code. It seems like an easy solution for your issues but if you'll get the whole point of React, you'll understand that jQuery is not the easy solution, especially in long run.
I have a redux store with a state called "Pets" which stores an array of the following:
[{"id"2,"name":"Cat"},
{"id":3,"name":"Dog"},
{"id":4,"name":"Fish"}
]
In my component, I map the states like this: (I'm using redux forms and react-bootstrap-table)
class PetsPage extends Component {
constructor(props) {
super(props);
this.state =({
selected:[]
});
this.showRow = this.showRow.bind(this);
}
componentWillMount() {
this.props.fetchPets();
}
render() {
const {
handleSubmit,
previousPage,
pets
} = this.props
if (!pets){
return (
<div className="container">
<div> Loading...</div>
</div>
);
}
return (
<div className="container">
<BootstrapTable
data={pets}
search={true}
striped={true}
hover={true}
condensed={true}
selectRow={selectRowProp}>
<TableHeaderColumn dataField="id" isKey={true}>Pet #</TableHeaderColumn>
<TableHeaderColumn dataField="name">Company</TableHeaderColumn>
</BootstrapTable>
<p> Currently Chosen: <span> id: {this.state.selected}</span> </p>
<div className="btn-group">
<button type="button" onClick={previousPage} className="btn btn-success btn-lg"><i/> Previous</button>
<button type="submit" className="btn btn-success btn-lg">Next</button>
</div>
</div>
</form>
)
}
}
function mapStateToProps(state) {
return { pets: state.pets.all };
}
export default reduxForm({
form: 'wizard',
fields,
destroyOnUnmount: false,
validate
}, mapStateToProps, actions)(PetsPage)
So far, everything WORKS.
My problem arises when I call the pets prop like this:
<p> Currently Chosen: {pets[2].name}</p>
I keep getting this error:
TypeError: pets[2] is undefined
Ideally I want to be able to call the name of the pet by the ID which is provided by the "selected" state.
EDIT ---
I noticed the code actually WORKS if I were to go to another page and come back to this page. This is kind of confusing because I thought having the ...loading container would prevent this from happening.
How do you obtain the initial state of pets? were those retrieved asynchronously? (which i suspect it is)
When your BootstrapTable initially load, the async call was not complete, pets was still undefined, hence the error. Then when you go to another page, and come back to this page, your async call was finished, pets is now an array, and it works.
If I am right so far, add something like this in your BootstrapTable:
if(this.props.pets) {
<p> Currently Chosen: {pets[2].name}</p>
... // render your pets props within the
} else {
// render some loading indicator
}
Edit: if your pets was initialized as an empty array, check the length of the pets instead of the truthy value
Shouldn't you be doing this.props.pets[2] ?
Or this.state.pets[2] ? Unsure exactly what's happening but I don't think pets[2] on it's own would ever be anything. If that doesn't help can you post some more of your code?
Ok, I just figured it out.
Following xiaofan2406's suggestion, I literally have to put
if(this.props.pets[0])
Because
if(this.props.pets)
will always be true.