How to search from React component rendered HTML string? - reactjs

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.

Related

React.js, correct way to iterate inside DOM

Im new in ReactJS...
I have a project with the following class components structure:
index.js
--app
--chat
--header
--left
--right
In the chat.js component, I make a google search with the api to retrieve images based on specific keyword... My intuitive solution was:
this.client.search("cars")
.then(images => {
for(let el of images) {
ReactDOM.render(<img src="{{el.url}}" syle="{{width: '100%'}}" />, document.querySelector('#gimages'));
}
});
It is correct? Or I may to use Components with stored states with flux (redux)?
Perhaps a simpler more conventional use of react would achieve what your require?
You could follow a pattern similar to that shown below to achieve what you require in a more "react-like" way:
class Chat extends React.Component {
constructor(props) {
super(props)
this.state = { images : [] } // Set the inital state and state
// model of YourComponent
}
componentDidMount() {
// Assume "client" has been setup already, in your component
this.client.search("cars")
.then(images => {
// When a search query returns images, store those in the
// YourComponent state. This will trigger react to re-render
// the component
this.setState({ images : images })
});
}
render() {
const { images } = this.state
// Render images out based on current state (ie either empty list,
// no images, or populated list to show images)
return (<div>
{
images.map(image => {
return <img src={image.url} style="width:100%" />
})
}
</div>)
}
}
Note that this is not a complete code sample, and will require you to "fill in the gaps" with what ever else you have in your current Chat component (ie setting up this.client)
This is not the way you should go, you don't need to use ReactDOM.render for each item. Actually, you don't need to use ReactDOM.render at all. In your component you can use a life-cycle method to fetch your data, then set it to your local state. After getting data you can pass this to an individual component or directly render in your render method.
class Chat extends React.Component {
state = {
images: [],
}
componentDidMount() {
this.client.search( "cars" )
.then( images => this.setState( { images } ) );
}
renderImages = () =>
this.state.images.map( image => <Image key={image.id} image={image} /> );
render() {
return (
<div>{this.renderImages()}</div>
);
}
}
const Image = props => (
<div>
<img src={props.image.url} syle="{{width: '100%'}}" />
</div>
);
At this point, you don't need Redux or anything else. But, if you need to open your state a lot of components, you can consider it. Also, get being accustomed to using methods like map, filter instead of for loops.

React setstate on big and small states

I have a state like this :
{
textfield: '',
data: [] //huge, used to render elements within the render()
}
When I want to update the textfield value (simple text input), I use this.setState({ textfield: newValue });. The problem is that there is some lag when I write a character in the field because it is re-rendering everything.
Is using shouldComponentUpdate() and deeply check my data object the only way to avoid re-rendering everything? Or is there a better/more efficient way?
Thanks
Am guessing its rerendering the entire component due to the state change on every key.
you could isolate your input element in a separate stateful component, hence only triggering a re-render on itself and not on your entire app.
So something like:
class App extends Component {
render() {
return (
<div>
...
<MyInput />
...
</div>
);
}
}
class MyInput extends Component {
constructor() {
super();
this.state = {textfield: ""};
}
update = (e) => {
this.setState({textfield: e.target.value});
}
render() {
return (
<input onChange={this.update} value={this.state.textfield} />
);
}
}

How to test a class change on a React component based on an event

I'm building a React application and I want to test certain functionality, however, I'm not sure how I should test it.
First of all I'm using React and ReactDOM version 15.4.2
I have a component which represents an input button.
class InnovanaInputBox extends React.Component {
constructor(props) {
super(props);
this.state = this.initializeState();
// Bind all the event handlers.
this.onChange = this.onChange.bind(this);
}
initializeState() {
return {
hasValue: false
}
}
onChange(event) {
this.setState(
{
hasValue: event.target.value !== ""
}
);
}
render() {
return (
<div className={"innovana-input-box" +
(typeof(this.props.className) !== typeof(undefined) &&
this.props.className !== "" ? " " + this.props.className: "") +
(this.state.hasValue ? " value" : "")}>
<input type="text" onChange={this.onChange} />
<label>{this.props.label}</label>
</div>
);
}
}
InnovanaInputBox.PropTypes = {
label: React.PropTypes.string.isRequired
}
export default InnovanaInputBox;
So, when I do enter a value in the input box inside the component, the state hasValue does change to true if the input box does contain a value.
In the render method, an additional class named value it set on the container component.
Now, how can I test this specific behaviour?
I have Karma and Mocha setup and I'm using the react-addons-test-utils version 15.4.2.
I do already have some basic tests to see if the component renders, but testing this seems a bit difficult.
I'm trying to play with Simulate and findReferedDOMComponentWithClass but I don't get it.
Any advice on how this can be tested?
Kind regads
you aren't specifying a ref on the class, so another way to get the element to test if an element has a class is to go about it like this.
const input = document.getElementsByClassName('innovana-input-box')[0];
input.classList.contains('value'); // true
Now if there is more than one you may want to either specify an id on the element and use document.getElementById() instead. or add a ref to the input and use that
More about refs here
EDIT:
if you want to test the state value on the component you can do something like this
const inputBox = TestUtils.renderIntoDocument(<InnovanaInputBox />);
const input = React.findDOMNode(inputBox).querySelector('input');
input.value = 'some updated value';
TestUtils.Simulate.change(input);
inputBox.state.hasValue // true

Draftjs components with props

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

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