ReactJS Toggling Props Length - reactjs

I have a show/hide functionality that I am building out for a component and I am wondering how I can toggle the length of a string from 5 characters to its full length and back based on a button click and the previous state. I have the button click and a boolean indicating a true/false state, but I am not sure how I can switch between the 5 character limit and full length. I can only get the expanded text and not the original state.
Based on a state boolean (showFullText) I thought of this solution:
if (this.state.showFullText == false){
partialText = this.props.discovery.slice(0, this.state.characterLimit);
} else {
partialText = this.props.discovery;
}
but it is not working within the context of this code. No error message.
import React from 'react';
//Annotation Card - Body
export default class Body extends React.Component {
constructor(props){
super(props);
this.state = { showFullText: false, characterLimit: 10 };
this.showHideFullText = this.showHideFullText.bind(this);
}
showHideFullText(){
console.log(this.state.showFullText);
this.setState({
showFullText: !this.state.showFullText,
characterLimit: this.props.discovery.length,
expandButtonText: "Show Less"
});
}
render() {
var partialText;
if (this.state.showFullText == false){
partialText = this.props.discovery.slice(0, this.state.characterLimit);
} else {
partialText = this.props.discovery;
}
var textExpandButton;
if(this.props.discovery.length >= this.state.characterLimit) {
textExpandButton = <TextExpandButton showHide={this.showHideFullText} showFullText={this.state.showFullText} />;
} else {
return this.props.discovery;
}
return (
<div>
<p>{partialText}</p>
{textExpandButton}
</div>
)
}
}
//Annotation Card - Body - Text Expand Link
export const TextExpandButton = props => {
var buttonText;
if(props.showFullText === true){
buttonText = "Show Less";
} else {
buttonText = "Show More...";
}
return <a href="#" onClick={props.showHide}>{buttonText}</a>
}

The approach to use a toggled boolean in state is a great one because of its simplicity.
In Body.showHideFullText, characterLimit is being set to the length of discovery and this.props.discovery.slice(0, this.state.characterLimit) really is this.props.discovery.slice(0, discovery.length).
characterLimit should possibly be a write once value (set to 5 as the initial state of Body component). I'll suggest making this a property of Body and setting its default value to 5. There is no reason to keep it in state with your present requirements for the component.
In Body.showHideFullText, only toggle the boolean value for showFullText
showHideFullText(){
this.setState(
prevState => ({
...prevState,
showFullText: !prevState.showFullText,
})
);
}
I don't find it necessary to store expandButtonText in the component state because its value can be decided from the value for showFullText.
Update: Code for affected components (Run on StackBlitz)
Body.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TextExpandButton from './TextExpandButton';
class Body extends Component {
constructor(props) {
super(props);
this.state = { showFullText: false };
this.toggleFullTextDisplay = this.toggleFullTextDisplay.bind(this);
}
toggleFullTextDisplay() {
this.setState(prevState => ({
...prevState,
showFullText: !prevState.showFullText,
}));
}
render() {
const { discovery, characterLimit } = this.props;
const { showFullText } = this.state;
return (
<div>
<p>{showFullText ? discovery : discovery.slice(0, characterLimit)}</p>
{discovery.length >= characterLimit && <TextExpandButton showFullText={showFullText} toggleFullTextDisplay={this.toggleFullTextDisplay} />}
</div>
)
}
}
Body.defaultProps = {
characterLimit: 5
};
Body.propTypes = {
characterLimit: PropTypes.number,
discovery: PropTypes.string.isRequired,
};
export default Body;
TextExpandButton.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const TextExpandButton = props => {
const { showFullText, toggleFullTextDisplay } = props;
const buttonText = showFullText ? "Show Less" : "Show More...";
return (
<a href="#"
onClick={props.toggleFullTextDisplay}
>
{buttonText}
</a>
);
}
TextExpandButton.propTypes = {
showFullText: PropTypes.bool.isRequired,
toggleFullTextDisplay: PropTypes.func.isRequired,
};
export default TextExpandButton;

Related

React Speech Recognition - inserting the text to the memory by updating the state

There is a similar question but I can't comment on it so I opening a new one.
I am new to React and try to implement React SpeechRecognition component for my app. The text should be in an input box. the code for it (from react doc [https://www.npmjs.com/package/react-speech-recognition][1] - with span tag instead of an input):
import React, { PropTypes, Component } from 'react'
import SpeechRecognition from 'react-speech-recognition'
const propTypes = {
// Props injected by SpeechRecognition
transcript: PropTypes.string,
resetTranscript: PropTypes.func,
browserSupportsSpeechRecognition: PropTypes.bool
}
class Dictaphone extends Component {
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props
if (!browserSupportsSpeechRecognition) {
return null
}
return (
<div>
<button onClick={resetTranscript}>Reset</button>
<span>{transcript}</span>
</div>
)
}
}
Dictaphone.propTypes = propTypes
export default SpeechRecognition(Dictaphone)
Now I try to update a state of text (a string) by the transcript (the words that have been already recognized) but I can't make it.
from an earlier question, someone suggested this:
<input
type="text"
value={transcript}
onChange={event => this.onInputChange(event.target.value)}
/>
now when I speak, I do see the words in the input box,
so the final code should be :
import React, { Component } from "react";
import PropTypes from "prop-types";
import SpeechRecognition from "react-speech-recognition";
const propTypes = {
// Props injected by SpeechRecognition
transcript: PropTypes.string,
resetTranscript: PropTypes.func,
browserSupportsSpeechRecognition: PropTypes.bool
};
class Dictaphone extends Component {
constructor() {
super();
this.state = {
text: '',
events: []
}
}
onInputChange = (event) => {
console.log (event.target.value);
this.setState( {text: event.target.value} );
}
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props;
if (!browserSupportsSpeechRecognition) {
return null
}
return (
<div>
<button onClick={resetTranscript}>Reset</button>
<input
className='bg-light-blue'
type="text"
value={transcript}
onChange={event => this.onInputChange(event.target.value)}
/>
</div>
)
}
}
Dictaphone.propTypes = propTypes;
export default SpeechRecognition(Dictaphone);
but when I console.log(event.target.value) which is text - I see nothing so I'm doing something wrong.
Note that if I just write in the render func:
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props;
var x = transcript;
console.log('x is ',x);
console.log('x length is: ',x.length);
.....
it does console the transcript (x) but it's not what I want - I need to save it in text by updating the state.
any suggestion?
If you need to store the transcript prop in your state you should do something like this.
componentDidUpdate(prevProps){
if(prevProps.transcript !== this.props.transcript){
this.setState({
text: this.props.transcript
});
}
}
In your render method use this.state.text to show in the input value.
Also in your constructor do
this.state = {
text: props.transcript
}

When button is clicked, previous state is not updated

I have this React Class component where I cannot get the Button text to change from on to off
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
on: true
}
}
handleClick = () => {
// todo
this.setState((prevState) => {
on: !prevState.on
})
}
render() {
const {
on
} = this.state;
return ( <
button onClick = {this.handleClick} >
{
on ? "On" : "Off"
} < /button>
);
}
}
ReactDOM.render( <
Toggle / > ,
document.getElementById('root')
);
I thought every time the button is clicked it would it would see the previous state and it would update.
You are just missing some parenthesis. If you want to implicitly return an object, you must put them.
handleClick = () => {
this.setState(prevState => ({
on: !prevState.on
}));
};
Check result here

ReactJS -- Unable to find latest title from an api causing error

I have a Landing component and a NewsLatest component. I am hitting on an api and trying to find the article with the latest timestamp but iam unable to get it done in reactJS.I checked the js code its working fine but in react it is not rendering. Kindly suggest something.
import React, { Component } from 'react'
import NewsSearch from '../NewsSearch/NewsSearch';
import NewsLatest from '../NewsLatest/NewsLatest';
import './Landing.css';
import axios from 'axios';
class Landing extends Component {
state={
newsList: []
}
componentDidMount(){
axios.get(`https://api.nytimes.com/svc/topstories/v2/home.json?api-key=7cK9FpOnC3zgoboP2CPGR3FcznEaYCJv`)
.then(res=> {
this.setState({newsList: res.data.results});
});
}
render() {
// console.log(this.state.newsList);
return (
<div className="landing text-center text-white">
<h1>News Portal</h1>
<div className="news-search">
<NewsSearch />
</div>
<div className="news-latest">
<NewsLatest newsList={this.state.newsList}/>
</div>
</div>
)
}
}
export default Landing;
import React, { Component } from 'react';
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props){
super(props);
this.state = {
newsTitle:'',
abstract:'',
newsUrl:'',
}
// this.newsLatest = this.newsLatest.bind(this);
}
newsLatest = (e)=>{
// e.preventDefault();
const {newsList} = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function (el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
if(latestnews){
this.setState({newsTitle: latestnews.title});
return (<h4>{this.state.newsTitle}</h4>);
}
}
newsTitle = () => (
this.props.newsList.map(item => (<h2 key={item.title}>{item.title}</h2>))
)
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
{this.newsLatest()}
</div>
);
}
}
export default NewsLatest;
There is some issue in rendering in NewsLatest component. KIndly suggest something.
Try this:
You must probably be getting a maximum depth error, use a lifecycle method instead like componentDidUpdate. Update your component state only if the previous props are different from the newer ones.
Read more here: https://reactjs.org/docs/react-component.html
import React, { Component } from "react";
// import PropTypes from 'prop-types';
class NewsLatest extends Component {
constructor(props) {
super(props);
this.state = {
newsTitle: "",
abstract: "",
newsUrl: ""
};
// this.newsLatest = this.newsLatest.bind(this);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.newsList !== this.props.newsList) {
const { newsList } = this.props;
let maxTime = newsList.map(function(o) {
return new Date(o.updated_date);
});
let maximumValue = Math.max(...maxTime);
let latestnews = newsList.filter(function(el) {
return maximumValue === new Date(el.updated_date).getTime();
})[0];
this.setState({ newsTitle: latestnews.title });
}
}
// newsLatest = e => {
// // e.preventDefault();
// const { newsList } = this.props;
// let maxTime = newsList.map(function(o) {
// return new Date(o.updated_date);
// });
// let maximumValue = Math.max(...maxTime);
// let latestnews = newsList.filter(function(el) {
// return maximumValue === new Date(el.updated_date).getTime();
// })[0];
// console.log(latestnews)
// if (latestnews && latestnews.hasOwnProperty('length') && latestnews.length>0) {
// return <h4>{this.state.newsTitle}</h4>;
// }
// };
newsTitle = () =>
this.props.newsList.map(item => <h2 key={item.title}>{item.title}</h2>);
render() {
console.log(this.props.newsList);
return (
<div>
<h2>News Latest....</h2>
<h4>{this.state.newsTitle}</h4>
</div>
);
}
}
export default NewsLatest;
Also, a sandbox: https://codesandbox.io/s/hungry-frog-z37y0?fontsize=14

Reactjs. Counter of renders

How to make counter of renders the child component in parent?
I have 2 components Widget (parent) and Message(child). I passed counter from child to parent and trying to set getting value from child set to state. And I getting err: Maximum update depth exceeded.
There is child component Message:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.changeCount = this.changeCount.bind(this);
this.state = { h: 0, counter: 0 };
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
changeCount = () => {
this.setState(state => ({
counter: ++state.counter
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.changeCount();
this.props.getCount(this.state.counter);
}
render() {
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
There is parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: state.counter
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
What I do wrong?
The answer by #Yossi counts total renders of all component instances. This solution counts how many renderes and re-renders an individual component has done.
For counting component instance renders
import { useRef } from "react";
export const Counter = props => {
const renderCounter = useRef(0);
renderCounter.current = renderCounter.current + 1;
return <h1>Renders: {renderCounter.current}, {props.message}</h1>;
};
export default class Message extends React.Component {
constructor() {
this.counter = 0;
}
render() {
this.counter++;
........
}
}
In order to count the number of renders, I am adding a static variable to all my components, and incrementing it within render().
For Class components:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
let renderCount = 0;
export class SampleClass extends Component {
render() {
if (__DEV__) {
renderCount += 1;
console.log(`${this.constructor.name}. renderCount: `, renderCount);
}
return (
<View>
<Text>bla</Text>
</View>
)
}
}
For functional Components:
import React from 'react';
import { View, Text } from 'react-native';
let renderCount = 0;
export function SampleFunctional() {
if (__DEV__) {
renderCount += 1;
console.log(`${SampleFunctional.name}. renderCount: `, renderCount);
}
return (
<View>
<Text>bla</Text>
</View>
)
}
The componentDidUpdate is calling this.changeCount() which calls this.setState() everytime after the component updated, which ofcourse runs infinitely and throws the error.
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
// Add a if-clause here if you really want to call `this.changeCount()` here
// For example: (I used Lodash here to compare, you might need to import it)
if (!_.isEqual(prevProps.color, this.props.color) {
this.changeCount();
}
this.props.getCount(this.state.counter);
}

ReactJS. Infinity loop

I'am getting props from child in getCount function. And set it prop into state. Than i try set it in component and get infinity loop. How can i fix that?
There is code of parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: count
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
child:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = { h: 0 };
this.counter = 0;
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
render() {
this.counter++;
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
The problem lies in your Message component.
You are using getCount() inside your componentDidUpdate() method. This causes your parent to re-render, and in turn your Message component to re-render. Each re-render triggers another re-render and the loop never stops.
You probably want to add a check to only run the function if the props have changed. Something like:
componentDidUpdate(prevProps) {
if(prevProps.color !== this.props.color) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
}
This will keep the functionality you need, but prevent, not only the infinity-loop, but also unnecessary updates.

Resources