React and forwardRef - reactjs

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.

Related

Warning: Failed prop type: Invalid prop `items[0]` of type `string` supplied to `ImageGallery`, expected `object`

I know this probably seems trivial, but I am a newbie and I am obviously missing something.
I am trying to create an array of what appear to be JSON objects to pass to react-image-gallery. See here NPM react-image-gallery
According to that web page, I need to do something like:
const images = [
{
original: 'https://picsum.photos/id/1018/1000/600/',
thumbnail: 'https://picsum.photos/id/1018/250/150/',
},
{
original: 'https://picsum.photos/id/1015/1000/600/',
thumbnail: 'https://picsum.photos/id/1015/250/150/',
},
{
original: 'https://picsum.photos/id/1019/1000/600/',
thumbnail: 'https://picsum.photos/id/1019/250/150/',
},
];
class MyGallery extends React.Component {
render() {
return <ImageGallery items={images} />;
}
}
However, note, I am not using the thumbnail, so I only need original.
Here is my code:
import React, { Fragment, useState } from 'react';
import axios from 'axios';
import ImageGallery from 'react-image-gallery';
//import '../image-gallery.css'
import "react-image-gallery/styles/css/image-gallery.css";
const AddNewItem = () => {
var [fileState, setFile] = useState([]);
var fileObj = {};
//bunch of irrelevant code
//Image Preview
const onChange = e => {
//setFile(e.target.files[0]);
fileObj = e.target.files[0];
console.log("AddNewItem fileObj: ", fileObj)
setFile(fileState => [...fileState, JSON.parse(`"orignal: ${URL.createObjectURL(fileObj)}"`)]);
}
//bunch of irrelevant code
return (
< Fragment >
{console.log("AddNewItem onChange fileState: ", fileState)}
<form id="myForm" onSubmit={onSubmit}>
<div className="container">
<div className="row ">
<div className="col">
<label className="mt-3 mb-0">Image:</label><br />
<input type="file" id="image" name="image" onChange={onChange}></input><br />
<button type="submit" className="mt-3">Submit</button>
</div>
<div className="col">
{fileState.length > 0 ? <ImageGallery items={fileState} showThumbnails={false} /> : ""}
</div>
</div>
</div>
</form>
</Fragment >
);
When I upload my first image, fileState is equal to the following, and I get the Failed prop type error:
When I upload a second image, fileState looks like:
And when there are 2 images in fileState, I do not get the error.
And no images are displayed by the gallery. I do get the arrow and play / pause button, but I do not see any images. But I am not entirely sure I have imported the image-gallery.css correctly according to the npm webpage mentioned above. But if that is an issue, then I will post another question on here.
Am I doing something wrong in the way that I am constructing fileState? If so, what can I do to fix it? Thanks.
You should pass an object to JSON.parse:
JSON.parse(`{"original": "${URL.createObjectURL(fileObj)}"}`)
Also, you have a typo, the key should be original.

How to pass an object with onClick React?

I have an object in an external file and I want to pass the url into the button onClick but I do not know to pass the value.
Object:
const ProjectLists = [
{
id: "4",
iconImage: "",
name: "Simple",
description: "Simple is a corporate responsive template and the absolutely clean & modern template theme for your business. The theme was built by Foundation Framework and take advantages of it features: grid system, typography, buttons, form, UI element, section and more.",
technologies: "HTML, CSS, JavaScript",
href: "http://striped-dolls.surge.sh"
}
]
export default ProjectLists;
How to pass ProjectLists.map((project, i) => href in map() into <button>
class Projects extends Component {
render() {
return (
<div>
{ProjectLists.map((project, i) =>
<section className='section'>
<div className='row'>
<div className='col-sm'>
<div className='content-left'>
<p key={project.id + i}>
{project.iconImage}
</p>
</div>
</div>
<div className='col-sm-8'>
<div className='content-right text-left'>
<h1>{project.name}</h1>
<p>{project.description}</p>
<p>Technologies: {project.technologies}</p>
<button type='submit' onClick=() => </button>>View live</button>
</div>
</div>
</div>
</section>
)}
</div>
)
}
}
Thank you for your help!
You can pass extra data in your map using arrow functions:
{ProjectLists.map(project =>
<button onClick={onProjectClick(project)}>View live</button>
}
// e.g.
onProjectClick = project => event => {
console.log(project.description);
navigateTo(project.href);
}
I noticed your button has type="submit" so more the correct event to handle is onSubmit on the form element however as this handles pressing the Return key for example (it's not essential though).
You can use "a" tag for this purpose.
<a href={project.href}></a>
If you want to use onClick in your button, then you can try this.
onClick={() => { window.location.href = project.href }}
state = {
redirect: false
}
setRedirect = (href) => {
window.location.href = href;
}
Call the setRedirect function
<button onClick={()=>this.setRedirect(project.href)}>View live</button>

checkbox hiding and showing component - react

I am building a small feature that has a checkbox styled as slider that, when turned on and off, should display another component - BatchWidget. The way I have it currently set up, it works on initial page load, and then hides as intended. However, when I go to "toggle" it back on to show the component again, it does not work. Is there an easy solution to this?
const Slider = (props) => {
return (
<div className="slider-container">
<label className="switch">
<input type="checkbox" checked={props.checked} onClick= {props.showWidget} />
<span className="slider round" />
</label>
<p className="batch-slider-title"> Batch Widget </p>
</div>
);
};
const Settings = ({showSlider}) => {
return (
<div className="settings">
<i className="icon-gear" onClick={() => showSlider()} />
</div>
);
}
class WidgetContainer extends Component {
constructor() {
super();
this.state = {
checked: true,
isSliderDisplayed: false,
};
this.showWidget = this.showWidget.bind(this);
this.showSlider = this.showSlider.bind(this);
}
showWidget() {
this.setState({
checked: !this.state.checked,
});
}
showSlider() {
this.setState({
isSliderDisplayed: !this.state.isSliderDisplayed,
});
}
render() {
const displayBatchWidget = this.state.checked ? <BatchWidget /> : null;
const displaySlider = this.state.isSliderDisplayed ? <Slider checked={this.state.checked} showWidget={this.showWidget} /> : null;
return (
<div>
<Settings showSlider={this.showSlider} />
{displaySlider}
{displayBatchWidget}
</div>
);
}
}
When I try to debug, it shows:
Warning: Failed form propType: You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`. Check the render method of `Slider`.
I think it is self-explanatory.
I've changed the line with checkbox to:
<input type="checkbox" checked={props.checked} onChange= {props.showWidget} />
Now, the batchWidget should hide and show on each click.
Reactjs matrial ui table check box hide
first do
<Table selectable={false}>
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
this method hide table header check box
then do <TableBody displayRowCheckbox={false}>
it hide table body checkbox
it work perfect.
reactjs

Can't focus on React input

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.

How to fix error Uncaught Invariant Violation: findComponentRoot(...?

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).

Resources