Reputation: 91
Edit: People from FUTURE: check out this video -
https://www.youtube.com/watch?v=FyxFSkh2wug and the article provided by Leonardo.
I have a searchbar component and a stockchart component that are both children of the Parent component. I want to re-render the stockchart every time a key is entered in the search bar. I am new to react so please bear with my code.
I tried a shouldComponentUpdate and it made too many API calls. In the CorrectRender function, I tried to return the StockChart everytime ticker changed but it didn't work.
I've tried my best to describe the methods I wrote, please feel free to ask any questions.
This is the Parent component, I'm calling this inside App.js
import React, { Component } from 'react'
import StockChart from './StockChart';
import SearchBar from './SearchBar';
// Renders stockChart when value is entered
// I tried to re-render by using a current and previousTicker symbols but it didn't work.
function CorrectRender(props, oldtick) {
const ticker = props.props;
console.log("props", props)
const previousTicker = props.oldtick;
if (ticker.length > 0 && previousTicker !== ticker) {
return <div>
<p>{ticker}</p>
<StockChart stockSymbol = {ticker} previousTicker={previousTicker}/>
</div>
}
return null;
}
export class Parent extends Component {
constructor(props) {
super(props);
this.state = {
stockSymbol: '',
previousTicker: ''
}
this.callbackFunction = this.callbackFunction.bind(this);
}
shaveData(value) {
let value1 = value.split(`,`);
value = value1[0];
this.setState({
stockSymbol: value,
});
}
callbackFunction(sS) {
this.setState({ previousTicker: this.state.stockSymbol});
this.shaveData(sS);
};
render() {
return (
<div>
<SearchBar parentCallback = {this.callbackFunction}/>
<CorrectRender props={this.state.stockSymbol} oldtick={this.state.previousTicker}/>
</div>
)
}
}
export default Parent;
This is the SEARCHBAR component.
import React, { Component } from 'react';
import './SearchBar.css';
export class SearchBar extends Component {
constructor(props) {
super(props);
this.state = {
search: ' ',
tickerList: [],
}
}
// Get a list of tickers from an API, this is not the api call that is being overused
// I want the data to show to the user as "Ticker, Company name" which is what the second (.then) is doing
fetchTickers(stockSymbol) {
const pointer = this;
let arr = [];
console.log(stockSymbol);
let url = `https://ticker-2e1ica8b9.now.sh/keyword/${stockSymbol}`;
fetch(url)
.then( function(response) {
return response.json();
})
.then ( function(data) {
for (var key in data) {
arr.push(data[key]['symbol'] + ", "+ data[key]['name'])
};
pointer.setState({
tickerList: arr
})
}
)
}
// this is for handling keys in the search bar and rendering new results based on the
critera
textChange = (e) => {
const value = e.target.value;
let arr = [];
if (value.length > 0) {
const regex = new RegExp(`^${value}`, 'i');
this.fetchTickers(value);
arr = this.state.tickerList.sort().filter(tick => regex.test(tick));
}
this.setState({
tickerList: arr,
search: value
});
}
// This will be called when an option is chosen by user
tickerChosen(value) {
let value1 = value.split(`,`);
value = value1[0];
this.setState({
search: value,
tickerList: []
});
};
// list of suggestions on user side based on criteria
tickerSuggestions () {
const { tickerList } = this.state;
if (tickerList.length === 0) {
return null;
}
return (
<ul>
{tickerList.map((ticker) => <li key={ticker} onClick={() => {this.props.parentCallback(ticker); this.tickerChosen(ticker);} }>{ticker}</li>)}
</ul>
)
};
render() {
const { search } = this.state;
return (
<div className= 'SearchBar'>
<input value = { search } onChange={this.textChange} type='text' />
{this.tickerSuggestions()}
</div>
)
}
}
export default SearchBar
This is the StockChart component.
import React, { Component } from 'react';
import Plot from 'react-plotly.js';
class StockChart extends Component {
constructor(props) {
super(props);
this.state = {
stockXValues: [],
stockYValues: [],
stockSymbol: '',
}
}
componentDidMount() {
this.fetchStock();
}
fetchStock() {
const pointer = this;
const symbol = this.props.stockSymbol;
const API_KEY = `X`;
let API_Call = `https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=${symbol}&outputsize=compact&apikey=${API_KEY}`;
let StockXValuesFunc = [];
let StockYValuesFunc = [];
fetch(API_Call)
.then(
function(response) {
return response.json();
}
)
.then(
function(data) {
console.log(data);
for (var key in data['Time Series (Daily)']) {
StockXValuesFunc.push(key);
StockYValuesFunc.push(data['Time Series (Daily)']
[key]['4. close']);
}
pointer.setState({
stockXValues: StockXValuesFunc,
stockYValues: StockYValuesFunc,
});
}
)
}
render() {
return (
<div>
<h2>{this.props.stockSymbol}</h2>
<Plot
data={[
{
x: this.state.stockXValues,
y: this.state.stockYValues,
type: 'scatter',
mode: 'lines+markers',
marker: {color: 'red'},
},
]}
layout={ {width: 720, height: 440, title: 'Closing Price over the last 100 days'} }
/>
</div>
)
}
}
Export default StockChart;
Upvotes: 1
Views: 464
Reputation: 13933
What you are looking for is Throttling or Debouncing
Throttling executes a given function after a specified amount of time has elapsed.
Debouncing enforces that a function will not be called again until a certain amount of time has passed since its last call. In debouncing, it ignores all calls to a function and waits until the function has stopped being called for a specified amount of time.
To apply throttling or debouncing you can use npm packages underscore,lodash or rxjs.
Throttling Example
// ...
import { throttle } from lodash;
class autocomp extends React.Component {
constructor(props) {
super(props);
this.state = {
results: []
}
this.handleInputThrottled = throttle(this.handleInput, 100)
}
handleInput = evt => {
const value = evt.target.value
const filteredRes = data.filter((item)=> {
// algorithm to search through the `data` array
})
this.setState({ results: filteredRes })
}
render() {
let { results } = this.state;
return (
<div className='autocomp_wrapper'>
<input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
<div>
{results.map(result=>{result})}
</div>
</div>
);
}
}
Debouncing Example
// ...
import { debounce } from 'lodash';
class autocomp extends React.Component {
constructor(props) {
super(props);
this.state = {
results: []
}
this.handleInputThrottled = debounce(this.handleInput, 100)
}
handleInput = evt => {
const value = evt.target.value
const filteredRes = data.filter((item)=> {
// algorithm to search through the `data` array
})
this.setState({ results: filteredRes })
}
render() {
let { results } = this.state;
return (
<div className='autocomp_wrapper'>
<input placeholder="Enter your search.." onChange={this.handleInputThrottled} />
<div>
{results.map(result=>{result})}
</div>
</div>
);
}
}
Upvotes: 0
Reputation: 11
Here you can find a good explanation on how to throttle and avoid doing so many API calls: Improve your react app performance by using throttling and debouncing
Upvotes: 1