Draftjs components with props - reactjs

I'm new to draftjs and I was wondering if there was a way to render my custom components inline in the editor.
I have a string with twitter handles. I use the decorator to detect regex #[{handle}] which replaces the handle and renders the component inline. However my handle component needs properties such as a callback function and a URL.
I'm not too sure how to pass my component the URL and callback function which I pass into my ContentEditable component.
I'm sure I'm just missing something. I've checked the contentState.getEntity(entityKey).getType() but it only sees the content I pass into the composite decorator as unstyled and not the decorated parts as separate blocks.
I've seen that you can modify the entity map, but I'm not sure if this is the right approach or how to define my own entity in the entity map
Does anyone know what I am missing to give properties to my component?
const decorator = new CompositeDecorator([
{
strategy: handleStrategy,
component: Handle,
},
]);
export default class ContentEditable extends component {
const content = 'some messages and my handle #[handle]';
if (this.props.content.trim() !== '') {
const processedHTML = DraftPasteProcessor.processHTML(content);
const entityMap = processedHTML.entityMap;
const contentState = ContentState.createFromBlockArray(processedHTML.contentBlocks, entityMap);
// Create with content with decorator
editorState = EditorState.createWithContent(contentState, decorator);
} else {
// Create empty content with decorator
editorState = EditorState.createEmpty(decorator);
}
this.state = {
editorState,
}
}
render() {
return (
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
ref="editor"
/>
);
}

I'm sorry the document is missing it. You can provide props in CompositeDecorator like CompositeDecorator({strategy:xxx,component:xxx,props:{...}})
Checking the source

Related

Get HTMLInputElement from ref to PrimeReact's InputText

I am trying to use a ref to get to the underlying input element of an InputText component. I used this.textFieldRef = React.createRef() to set up the ref, and then the attribute ref={this.textFieldRef} to hook it up. Yet in the componentDidMount I cannot use this.textFieldRef.current.select() because select() is not a function available for that object. So somehow, InputText is not returning the underlying HTMLInputElement.
Does anyone know how I can get from a ref to something that allows me to select() the text in the InputText element?
Here is my code, which is actually in TypeScript...
import * as React from 'react';
import { InputText } from 'primereact/inputtext';
export class ValueCard extends React.Component<{}, {}> {
textFieldRef: React.RefObject<any> = React.createRef();
componentDidMount = () => {
if (this.textFieldRef.current instanceof InputText) {
this.textFieldRef.current.select();
}
}
render() {
return = (
<InputText
value="test"
ref={this.textFieldRef}
/>
);
}
}
Looking at the source for PrimeReact's InputText component (source), they are attaching a reference to the inner input element to this.element.
This allows you to just add .element to your reference:
this.textFieldRef.current.element.select();
I tested it out in this sandbox, and it seems to work as expect:
https://codesandbox.io/s/203k7vx26j
Maybe you could try to use react-dom library:
ReactDOM.findDOMNode(this.textFieldRef.current).querySelector('input');

Render React component from instantiated React.Component

I have a couple of React components that are all based of the same base class, these component have a couple of properties which I would like to read before I render the component. This has to do with some conditions that are used somewhere else.
Currently I am calling a method, with something like this in my Render function.
public getWidget(): JSX.Element {
let widget = null;
switch (widgetType) {
case 'widget1': {
widgetComponent = new Widget1(props); // private variable in my class
widget = (<Widget1 { ...props } ref = { some-ref });
}
case 'widget2': {
widgetComponent = new Widget2(props); // private variable in my class
widget = (<Widget2 { ...props } ref = { some-ref });
}
}
return widget;
}
This way I can ask the widget some stuff about it's default values and render the widget variable in my Render function, like this:
render() {
const widget = this.getWidget();
const somethingIWantToKnow = this.widgetComponent.someProperty;
return ({ widget });
}
From what I understand, the reference I set for my React Component is only available after I render? Otherwise I could just use that.
I also tried calling this.widgetComponent.render() in my own Render method, but this does not set up the component correctly (probably because of missing componentWillMount and componentDidMount calls.
I just can't believe this is the way to go, is there a way to render from this.widgetComponent in my Render method, or is there a way to get properties from the class behind a JSX.Element?
NULL checks and other stuff is all removed from these code snippets :)
Give your widget a ref,
widget = (<Widget1 { ...props } ref = { widget1 } />);
Then you can access your instantiated component in componentDidMount and use the ref to access the property,
componentDidMount(){
const somethingIWantToKnow = this.widget1.current.someProperty
}

How to search from React component rendered HTML string?

Hi I am working on a react-based table, which should support search functionality. Pseudo code like below:
export default class MyTable {
constructor() {
this.onSearch = (event) => {
const value = event.target.value;
/* stuck here */
}
}
return (
<MySearch onSearch={this.onSearch} />
<MyTableBody />
);
}
MyTableBody is responsible to translate text into <tr><td> HTML elements. I am stuck at two points:
How can I get MyTableBody rendered HTML string so that I can use it in
this.onSearch method?
How to retrieve text only from HTML string?
Just like the text() method used in jest/enzyme.
What you did was what we would normally do with JQuery.
With React, it's bit different.
export default class MyTable {
constructor() {
super();
this.state = {
keyword: undefined
}
}
onSearch = (event) => {
this.setState({keyword: event.target.value});
}
render() {
return (
<MySearch onSearch={this.onSearch.bind(this)} />
<MyTableBody searchKeyword={this.state.keyword} />
)
}
}
"onSearch" should be called when you typing in the textbox inside 'MySearch' component.
Now 'searchKeyword' prop gets updated in the MyTableBody component through the state variable 'keyword'
Then you must have a filter method in the MyTableBody component to filter the Data object/array used to render the table.
I have not tested this but concept should be like that
You should change the way you think when working with React.
We should use the re-rendering functionality to do the DOM manipulation.
You never have to use something like JQuery ever again.

Extending react custom component

I have a react component called editPhoto.js it has a onDeleteFile method that I want to override when I extend it. The problem is editPhoto.js is connected to a redux store. Is inheritance the wrong approach here ? Or how would I go about extending and overriding the EditPhoto onDeleteFile and save methods ?
UPDATE: 11-1
editPhoto.js does all the same functionality that editBlog needs to do. So in OOP terms this is a classic case for inheritance by extending editPhoto with editBlog so editBlog inherits all of editPhoto's functions and properties. With inheritance I could also override two methods that editBlog uses differently from editPhoto. The save and onDeleteFile functions.
Here is how both those functions are used differently.
save() method should call a different action:
save(){
all the same (save) method code in editPhoto goes here....
// this.props.editPhoto(formData, this.props.history) should be changed to
this.props.editBlog(formData, this.props.history).
}
onDeleteFile() method should call a different action:
onDeleteFile(){
all the same (onDeleteFile) method code in editPhoto goes here....
// this.props.editBlog(this.state.id, this.props.history) should be changed to
this.props.deletePhoto(this.state.id, this.props.history)
}
Thats the only two changes in the code which seems crazy to have to duplicate 112 lines of code for just 2 lines to be changed.
Id' also like to know how you would override a method using composition but its not necessary in this case (maybe best for another post).
If there is a way of doing this with composition I'm totally open to not using inheritance as it wont inherit when using the Redux connect in EditPhoto.
END OF UPDATE 11-1 -------------------------------
See all code below trying to do the same thing in inheritance.
import React, {Component} from 'react';
import EditPhoto from './editPhoto';
export default class EditBlog extends EditPhoto{
constructor(props){
super(props);
}
// override original EditPhoto
onDeleteFile(event){
event.preventDefault();
console.log("DeleteBlog : onDeleteFile");
this.props.deleteBlog(this.state.id, this.props.history)
}
save( event ){
event.preventDefault();
const formData .....
// call different action for editBlog(formData, this.props.history);
}
}
Here is my editPhoto.js code
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Dropzone from 'react-dropzone';
import FieldEditor from '../../components/admin/fieldEditor';
import DropzoneArea from '../../components/admin/dropzoneArea';
import {editPhoto, deletePhoto} from '../../actions/index';
import style from '../../../styles/admin/editPhoto.scss';
class EditPhoto extends Component{
constructor(props){
super(props);
this.onUpdateState = this.onUpdateState.bind(this);
this.handleDrop = this.handleDrop.bind(this);
this.save = this.save.bind(this);
this.onDeleteFile = this.onDeleteFile.bind(this);
this.state = {
fieldEdits:'',
preview:'',
file:'',
id:''
}
}
componentWillMount(){
if(this.props.location.state)
this.setState({
preview: this.props.location.state.photo.photo,
fieldEdits: this.props.location.state.photo,
id: this.props.location.state.photo._id
})
}
onUpdateState(fieldEdits){
this.setState({fieldEdits});
}
handleDrop(acceptedFiles, rejectedFiles){
acceptedFiles.forEach( (file, index) => {
const reader = new FileReader();
reader.onload = ( event ) => {
const img = event.target.result;
this.setState({preview:img, file});
};
reader.onabort = () => console.log('file reading was aborted');
reader.onerror = () => console.log('file reading has failed');
reader.readAsDataURL(file);
});
}
onDeleteFile(event){
event.preventDefault();
this.props.deletePhoto(this.state.id, this.props.history);
}
save( event ){
event.preventDefault();
var form = document.forms.namedItem("photoEditUpload");
var formData = new FormData(form);
if(this.state.file.name){
formData.append('photos', this.state.file, this.state.file.name);
}
formData.append('id', this.state.id);
// for (var pair of formData.entries()) {
// console.log(pair[0]+ ' = ' + pair[1]);
// }
this.props.editPhoto(formData, this.props.history);
}
render(){
if(!this.props.location.state){
// TO DO : ADD IN A ERROR COMPONENT FOR THIS.
return <div>No photo to edit. GO BACK !</div>
}
return(
<div id="edit-photo" className='container'>
<form onSubmit={this.save} ref='form' role="save" encType="multipart/form-data" name="photoEditUpload" method="post">
<div className="row">
<div className="photo-container col-sm-8">
<img src={this.state.preview} className="edit-photo"></img>
</div>
<div className="col-sm-4">
<div>
<Dropzone onDrop={this.handleDrop} multiple={false} accept="image/jpeg" className="dropzone">
<DropzoneArea />
</Dropzone>
</div>
<div className="delete-container">
<button onClick={this.onDeleteFile} className='btn btn-primary' type='button'>Delete</button>
</div>
</div>
</div>
<FieldEditor callback={this.onUpdateState} {...this.state.fieldEdits} />
<div className="button-container">
<button className='btn btn-primary btn-save' type='submit'>Save</button>
</div>
</form>
</div>
)
}
}
export default connect(null, {editPhoto, deletePhoto})(EditPhoto);
NOTE:
Here is a stripped down version in jsfiddle showing the issue after palsrealm suggested passing the context so the store would not be undefined.
https://jsfiddle.net/chapster11/jzprkx96/31/
Your problem is that you are extending the component created by
export default connect(null, {editPhoto, deletePhoto})(EditPhoto);
If you look into the react-redux documentation, you can see that connect returns the Connect component, which extends React.Component.
When you declare
export default class EditBlog extends EditPhoto {
it might look like you are extending class EditPhoto but you're actually extending Connect. Connect wraps EditPhoto by composition and has no idea what EditPhoto's methods and properties are. There is no inheritance chain to help you out. Connect is also getting pissed off and throwing errors because it doesn't expect to get used like this.
The naming of your import at import EditPhoto from './editPhoto'; is misleading.
If you want to extend the class EditPhoto, you need to export the class itself
export class EditPhoto extends Component {
and import that in your EditBlog module
import {EditPhoto} from './editPhoto';
export default class EditBlog extends EditPhoto {
BUT - I wouldn't recommend doing this.
Inheritance is an antipattern in React. Check out the React docs on Composition vs Inheritance
At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.
So... I would step back and try to solve the problem again with composition.
Update - Composition Example
Based on the extra detail you give in your update, I would probably start with a generic EditAsset component something like...
class EditAsset extends React.Component {
static propTypes = {
deleteAsset: PropTypes.func.isRequired,
updateAsset: PropTypes.func.isRequired,
...any other asset-specific props
}
handleDelete = () => {
...do generic stuff...
this.props.deleteAsset(id, whatever, whatever)
}
handleSave = () => {
...do generic stuff...
this.props.updateAsset(id, whatever, whatever)
}
render = () => {
...all your generic render stuff...
}
}
Then you can use redux connect to compose different components to deal with specific asset types. e.g.
import {EditAsset} from './path/to/EditAsset';
import {deletePhoto, updatePhoto, deleteBlog, editBlog} from './path/to/actions';
export const EditPhoto = connect(
undefined,
dispatch => ({
deleteAsset: (...args) => dispatch(deletePhoto(...args)),
updateAsset: (...args) => dispatch(updatePhoto(...args)),
})
)(EditAsset);
export const EditBlog = connect(
undefined,
dispatch => ({
deleteAsset: (...args) => dispatch(deleteBlog(...args)),
updateAsset: (...args) => dispatch(updateBlog(...args)),
})
)(EditAsset);
So, you write the generic component once and use composition to create one, two, one thousand specific components with appropriate data and methods passed down as props.
In this example we are just passing down two action dispatchers but you could obviously use mapStateToProps to pass down whatever specific data you need.
Does that make sense?
The store is passed around in Redux using the context, which is then connected to the class using the connect HOC. So, if you want your EditBlog component to have access to the store you need to pass the context in the constructor.
constructor(props,context){
super(props,context);
}
The call to super in the constructor creates the this keyword for the class. When you pass the context in the constructor and then to the super, the context is also used to create the this for the class along with the props. This will give access to the store to the EditBlog class.
More information at : https://reactjs.org/docs/context.html
Example code : https://codesandbox.io/s/1qrnmqror3
Edit 11/1: As described in #MarcDavies answer there is no inheritance chain to help you achieve what you are trying to do. Instead you should use composition. To use composition you can redesign your components such that the parts that are common are in one component and the parts that change are in different components.
I do not know enough about your components to make an informed design but from what I could get from your update, you are going to vary the functionality of the delete button based on whether it is a photo or a blog. To do that you can create a DeletePhoto button and a DeleteBlog button which would get the common delete functionality as props. The common parent component would know whether it is a blog or a photo and render the appropriate button accordingly.

is there any way to access the parent component instance in React?

I know it's not a functional approach to be able to do something like this.parent in a React component, and I can't seem to find any properties on a React component instance that lead to the parent, but I'm just looking to be able to do some custom things where I need this.
Before anyone wastes their time explaining it's not the functional React "way," understand that I need this because of the following I'm trying to achieve:
Build a transpiler for Meteor's Spacebars templating engine, whose rendering model does take into consideration parent components/templates.
I've already built a transpiler that modifies the output jsx to achieve this. I do this by passing in parent={this} in all child components composed. However, after the fact it occurred to me that maybe I simply don't know of something that will give me a way to access the parent component instance without additional transpilation modifications.
Any tips would be much appreciated.
There's nothing wrong if you need to access the parent's props and functions from the children.
The point is that you should never use React internals and undocumented APIs.
First of all, they are likely to change (breaking your code) and, most importantly, there are many other approaches which are cleaner.
Passing props to children
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
const Child = ({ fn }) => <button onClick={fn}>Click me!</button>
Working example
Using context (if there's no direct parent/child relation)
class Parent extends React.Component {
constructor(props) {
super(props)
this.fn = this.fn.bind(this)
}
getChildContext() {
return {
fn: this.fn,
}
}
fn() {
console.log('parent')
}
render() {
return <Child fn={this.fn} />
}
}
Parent.childContextTypes = {
fn: React.PropTypes.func,
}
const Child = (props, { fn }) => <button onClick={fn}>Click me!</button>
Child.contextTypes = {
fn: React.PropTypes.func,
}
Working example
Update for React 0.13 and newer
Component._owner was deprecated in React 0.13, and _currentElement no longer exists as a key in this._reactInternalInstance. Therefore, using the solution below throws Uncaught TypeError: Cannot read property '_owner' of undefined.
The alternative is, as of React 16, this._reactInternalFiber._debugOwner.stateNode.
You've already recognized that this is not a good thing to do almost always, but I'm repeating it here for people that don't read the question very well: this is generally an improper way to get things done in React.
There's nothing in the public API that will allow you to get what you want. You may be able to get to this using the React internals, but because it's a private API it's liable to break at any time.
I repeat: you should almost certainly not use this in any sort of production code.
That said, you can get the internal instance of the current component using this. _reactInternalInstance. In there, you can get access to the element via the _currentElement property, and then the owner instance via _owner._instance.
Here's an example:
var Parent = React.createClass({
render() {
return <Child v="test" />;
},
doAThing() {
console.log("I'm the parent, doing a thing.", this.props.testing);
}
});
var Child = React.createClass({
render() {
return <button onClick={this.onClick}>{this.props.v}</button>
},
onClick() {
var parent = this._reactInternalInstance._currentElement._owner._instance;
console.log("parent:", parent);
parent.doAThing();
}
});
ReactDOM.render(<Parent testing={true} />, container);
And here's a working JSFiddle example: http://jsfiddle.net/BinaryMuse/j8uaq85e/
Tested with React 16
I was playing around with something similar using context, tho to anyone reading this, for most usual cases, accessing the parent is not advised!
I created a holder that when used, would always have a reference to the first holder up the display list, so its 'parent' if you will. Looked something like this:
const ParentContext = React.createContext(null);
// function to apply to your react component class
export default function createParentTracker(componentClass){
class Holder extends React.PureComponent {
refToInstance
render(){
return(
<ParentContext.Consumer>
{parent => {
console.log('I am:', this, ' my parent is:',parent ? parent.name : 'null');
return(
<ParentContext.Provider value={this}>
<componentClass ref={inst=>refToInstance=inst} parent={parent} {...this.props} />
</ParentContext.Provider>
)}
}
</ ParentContext.Consumer>
)
}
}
// return wrapped component to be exported in place of yours
return Holder;
}
Then to use it you would pass your react component to the method when you export it like so:
class MyComponent extends React.Component {
_doSomethingWithParent(){
console.log(this.props.parent); // holder
console.log(this.props.parent.refToInstance); // component
}
}
// export wrapped component instead of your own
export default createParentTracker(MyComponent);
This way any component exporting the function will get its parent's holder passed in as a prop (or null if nothing is further up the hierarchy). From there you can grab the refToInstance. It will be undefined until everything is mounted though.

Resources