How to store jsx into a variable using react? - reactjs

I am new to programming. I want to implement the below,
when I click a list item I want to navigate to another page.
What I am trying to do? I have list items in a side panel. when I click on the list item it should navigate to another page.
So I have a listpanel component which renders the listitem component. On click list item, based on item_data_type it should take to the link got from get_type1_link method. however, it returns an object. I am not sure where I am making mistake.
class ListPanel extends react.purecomponent {
get_type1_link = () => {
const item_data = this.props.item_data;
const itemss = this.props.items;
const {itemname, item_id} = item_data.properties;
const filtered_item = items && items.find(item => item.id ===
item_id);
const item_name = (filtered_item) ? filtered_item.itemname :
(itemname ? itemname : item_id);
if (filtered_item) {
return (<Link to={`/someurl/${item_data.properties.item_id}`}>
{item_name}</Link>);
} else {
return <span>{item_name}</span>;
}
};
get_link = () => {
const item_data = this.props.item_data;
let link;
switch (item_data.type) {
case 'type1':
link = this.get_type1_link();
break;
case 'type2':
link = this.get_type2_link(); //some method similar to
//get_type1_link method
break;
default:
return link=window.location.href;
}
return link;
};
render = () => {
const list_item = this.props.;
return (
<ListItem
key={list_item.id}
text={this.get_text}
link={this.get_link}/>
);
}
class ListItem extends react.purecomponent {
render = () => {
<li onClick={props.link}>
<div className="text">
{this.props.text}
</div>
</li>
}
}
I think there is a problem in the way I am storing the value returned from get_type1_link method into variable link. since get_type1_link returns a jsx (Link). Could someone help me with this thanks.

I think issue is with your extends,
class ListPanel extends react.purecomponent {
It should be
class ListPanel extends React.PureComponent {
And also another issue is your render function, you have not return anything. Your render function should return like,
class ListItem extends React.PureComponent {
render = () => {
return(
<li onClick={this.props.link}> //props.link should be this.props.link
<div className="text">
{this.props.text}
</div>
</li>
)
}
}

Related

Cannot call a child component's function from the parent component. Error: "...is not a function"

For my website I want to include a feature that helps users randomly click a link programatically. The event happens in the parent component called StreamingPlaza, and its has a list of children components called StreamingCard, each containing a streaming link. Below is my code:
Streaming Plaza
class StreamingPlaza extends Component {
state = {
......
}
roomclicks = React.createRef([]);
componentDidMount() {
this.roomclicks.current[0].handleClick();
}
renderRoom = (room) => {
return <StreamingCard info={room} ref={(ref) => {this.roomclicks.current[0] = ref}}></StreamingCard>;
render() {
const rooms = this.props.rooms;
return (
{ rooms && rooms.map (room => {
return this.renderRoom(room);
})
}
);
}
}
Streaming Card
class StreamingCard extends Component {
constructor(props){
super(props);
this.state = {
......
}
handleClick = () => {
console.log("called");
document.getElementById("link").click();
}
render() {
return (
✔️ Streaming Link: <a id="link" href=......></a>
);
}
}
I got the error "this.roomclicks.current[0].handleClick is not a function." I looked through many relevant stackoverflow posts, and the answers suggested that this code was supposed to work. I would appreciate a lot if someone can tell me where I get it wrong. Thanks!

React: Add active class to selected Nav link on click

I'm trying to add an 'active' class to a nav link when it is clicked.
in JQuery I could do something like:
$('.navlink').on('click', function() {
$("a").removeClass('active');
$(this).toggleClass('active');
});
This is the closet I've managed to get in React:
export class NavLink extends Component {
constructor(props) {
super(props);
this.toggleClass=this.toggleClass.bind(this);
this.state = {
isActive: 1,
}
}
toggleClass = () => {
this.setState({ isActive: this.props.id });
}
render() {
const { href, name, id } = this.props;
const classNames = this.state.isActive === id ? "nav-link active" : "nav-link";
return (
<a
className={classNames}
onClick={this.toggleClass.bind(this)} href={href}>{name}</a>
)
}
}
The problem is React only re-renders the clicked on link. So once a link has been given a class of active it won't be removed if a different link is clicked.
You would need to do something like this in terms of components:
NavLinkList
|--NavLink
|--NavLink
NavLinkList would be the one holding the activeLinkId in its state.
That activeLinkId would then be passed to each Link as prop so when it is changed the Links would re-render.
Also in that NavLinkList you would have the function which would change the activeLinkId on the Links onClick handler.
I've done something like this before. It is easier to do this through the parent component. I didn't have components for each of my links. I just set their classes before the return in the render. Here is a sample of the code I did that I hope can be helpful to you.
I did the same thing with the onClick and the state just held a selected which held a String representing the name of the nav item. I had a Stories, Comments, and Likes nav links. Their original class was just 'tab'.
render () {
let storiesClass = 'tab';
let commentsClass = 'tab';
let likesClass = 'tab';
if (this.state.selected === "Stories") {
storiesClass += 'Selected';
commentsClass = 'tab';
likesClass = 'tab';
} else if (this.state.selected === "Comments") {
storiesClass = 'tab';
commentsClass += 'Selected';
likesClass = 'tab';
} else {
storiesClass = 'tab';
commentsClass = 'tab';
likesClass += 'Selected';
}
return (
<button className={storiesClass} onClick={this.selectTab("Stories")}>...

How to get the DOM node from a Class Component ref with the React.createRef() API

I have these two components:
import { findDOMNode } from 'react-dom';
class Items extends Component {
constructor(props) {
super(props);
this.ref = React.createRef();
this.selectedItemRef = React.createRef();
}
componentDidMount() {
if (this.props.selectedItem) {
this.scrollToItem();
}
}
componentWillReceiveProps(nextProps) {
if (this.props.selectedItem !== nextProps.selectedItem) {
this.scrollToItem();
}
}
scrollToItem() {
const itemsRef = this.ref.current;
const itemRef = findDOMNode(this.selectedItemRef.current);
// Do scroll stuff here
}
render() {
return (
<div ref={this.ref}>
{this.props.items.map((item, index) => {
const itemProps = {
onClick: () => this.props.setSelectedItem(item.id)
};
if (item.id === this.props.selectedItem) {
itemProps.ref = this.selectedItemRef;
}
return <Item {...itemProps} />;
})}
</div>
);
}
}
Items.propTypes = {
items: PropTypes.array,
selectedItem: PropTypes.number,
setSelectedItem: PropTypes.func
};
and
class Item extends Component {
render() {
return (
<div onClick={() => this.props.onClick()}>item</div>
);
}
}
Item.propTypes = {
onClick: PropTypes.func
};
What is the proper way to get the DOM node of this.selectedItemRef in Items::scrollToItem()?
The React docs discourage the use of findDOMNode(), but is there any other way? Should I create the ref in Item instead? If so, how do I access the ref in Items::componentDidMount()?
Thanks
I think what you want is current e.g. this.selectedItemRef.current
It's documented on an example on this page:
https://reactjs.org/docs/refs-and-the-dom.html
And just to be safe I also tried it out on a js fiddle and it works as expected! https://jsfiddle.net/n5u2wwjg/195724/
If you want to get the DOM node for a React Component I think the preferred way of dealing with this is to get the child component to do the heavy lifting. So if you want to call focus on an input inside a component, for example, you’d get the component to set up the ref and call the method on the component, eg
this.myComponentRef.focusInput()
and then the componentRef would have a method called focusInput that then calls focus on the input.
If you don't want to do this then you can hack around using findDOMNode and I suppose that's why it's discouraged!
(Edited because I realized after answering you already knew about current and wanted to know about react components. Super sorry about that!)

Should componentDidMount run from connectStateResults?

I'm trying to create an infinite siema carousel using algolia's instantsearch in react, but I don't think the connectors behave like React components. Should I expect componentDidMount to be called here? Suggestions? Ideas?
class ActorsClass extends connectStateResults {
constructor(props){
super(props);
var { searchState, searchResults } = props;
this.hasResults = searchResults && searchResults.nbHits !== 0;
}
componentDidMount() {
console.log("componentDidMount " + this.props.siema)
this.siema = new Siema(this.props.siema);
}
prev = () => {
this.siema.prev()
};
next = () => {
this.siema.next()
};
render = () => {
return (
<div className="actors-container">
<div xhidden={!this.hasResults}>
<h1>Actors</h1>
<InfiniteHits hitComponent={HitActors} />
</div>
<button onClick={this.prev}>Prev</button>
<button onClick={this.next}>Next</button>
</div>
);
}
Whenever the connected component receives new props they are re-invoked. It means you can use componentDidUpdate hook for your use case.
You may be interested to use reselect. See the docs for using selector.

Rendering in react with array.map

I have an array of strings which I would like to render as a list, with a colored text. The user can change the color with a button.
For that I have built a component called which receives an array and renders a list with the array's values and a button to change the color:
import React, { Component } from "react";
const renderArray = arr => (arr.map(value => (
<li>
{value}
</li>
)))
class List extends Component {
constructor(props) {
super(props);
this.state = {
color: 'red'
}
}
toggleColor = () => {
if (this.state.color === "red") {
this.setState({color: "blue"});
} else {
this.setState({color: "red"});
}
}
render() {
const style = {
color: this.state.color
};
return (
<div style={style}>
<ul>
{renderArray(this.props.array)}
</ul>
<button onClick={this.toggleColor}>Change color</button>
</div>
);
}
}
export default List;
The List is called with:
<List array={arr} />
And arr:
const arr = ['one', 'two', 'three'];
Fiddle here: Fiddle
But this seems incorrect to me. I rerender the whole array by calling renderArray() each time the color changes. In this case it is not too bad but what if the renderArray() is much more complex?
To my understanding, I need to create a new list only if the array prop changes and this could do in getDerivedStateFromProps (or in componentWillReceiveProps which will be deprecated...):
componentWillReceiveProps(nextProps)
{
const renderedArray = renderArray(nextProps.array);
this.setState({ renderedArray });
}
And then, on render, use this.state.renderedArray to show the list.
But this seems strange, to store a rendered object in the state...
Any suggestions?
Thanks!
1) React uses the concept of virtual DOM to calculate the actual difference in memory and only if it exists, render the difference into DOM
2) You can "help" React by providing a "key", so react will better understand if it's needed to re-render list/item or not
3) Your code componentWillReceiveProps can be considered as a bad practice because you're trying to make a premature optimization. Is repaint slow? Did you measure it?
4) IMHO: renderArray method doesn't make sense and can be inlined into List component
React render the DOM elements efficiently by using a virtual DOM and checks if the update needs to happen or not and hence, it may not be an issue even if you render the list using props. To optimise on it, what you can do is to make use of PureComponent which does a shallow comparison of state and props and doesn't cause a re-render if nothing has changed
import Reactfrom "react";
const renderArray = arr => (arr.map(value => (
<li>
{value}
</li>
)))
class List extends React.PureComponent { // PureComponent
constructor(props) {
super(props);
this.state = {
color: 'red'
}
}
toggleColor = () => {
if (this.state.color === "red") {
this.setState({color: "blue"});
} else {
this.setState({color: "red"});
}
}
render() {
const style = {
color: this.state.color
};
return (
<div style={style}>
<ul>
{renderArray(this.props.array)}
</ul>
<button onClick={this.toggleColor}>Change color</button>
</div>
);
}
}
export default List;

Resources