React Call Youtube on Exit of Input - reactjs

Im trying to use youtubes API with the you-tube-api search library, but am having some trouble getting new searches to happen.
I created a new function called search_youtube that I want to be called when a user exits the input. The way I have it setup now, the function is called continuously when I load the html page.
What is the appropriate way to solve this so that when a user exits the input a new search is rendered.
class SearchBar extends Component {
constructor(props) {
super(props)
this.state = {
videos: [],
selectedVideo: null
}
}
searchYoutube(event) {
console.log("called")
YTSearch({ key: API_KEY, term: event.target.value }, (videos) => {
this.setState({
videos: videos,
selectedVideo: videos[0]
})
});
}
render() {
return(
<div className="search-bar">
<input onBlur={this.search_youtube(event)} />
</div>
)
}
}

They are loading everytime you load your page because it's inside your render function, you are calling that function instead of passing the function to onBlur, change that line to this:
<input onBlur={event => this.search_youtube(event)} />
Obs. If you want to learn more about arrow functions check this article.

Related

I wanna console.log the value after clicking the submit button once and to delete the previous mapped items, but it doesnt work

I'm very new to react and I got two problems:
I want to console log the input and display the mapped data after clicking the submit button once. But I get console logged the input and the mapped data after clicking the button twice.
I wanna clear the mapped list (data from previous input) and display new list items depending on the input. But the new list items are only added to the end of the previous list (only the last list item from the previous list got overwritten by the first list item of the new list).
So this is the code from my app component:
import React, { Component, Fragment } from 'react';
import './App.css';
import Display from './/Display';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
passedValue: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
this.setState({ passedValue: this.state.value });
console.log(this.state.passedValue);
event.preventDefault();
}
render() {
return (
<div>
<form className="inputContainer" onSubmit={this.handleSubmit}>
<input type="text" name="company_name" onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<Display listDataFromParent={this.state.passedValue} />
</div>
);
}
}
export default App;
And this is my display component:
import React, { Component } from 'react'
import "./Display.css";
export default class Display extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: []
};
}
componentWillReceiveProps() {
fetch("http://localhost:5000/company?company_name=" + this.props.listDataFromParent)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: result
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, data } = this.state;
// if (error) {
// return <div>Error: {error.message}</div>;
// } else if (!isLoaded) {
// return <div>Loading...</div>;
// } else {
return (
<div className="display">
<h1>Kreditnehmer</h1>
<ul>
{this.props.listDataFromParent}
{data.map(item => (
<li key={item.c.company_id}>
Relation type: {item.r.relation_group}
Last name: {item.p.last_name}
</li>
))}
</ul>
</div>
);
}
}
Can anyone help?
1) setState is async method in react means it will take some time to update the component state. You can get your console log by using callback function of setState like
this.setstate({ value: e.target.value }, () => { console.log(this.state.value) });
2) in display component, your using componentWillReciveProps life cycle and inside that your using this.props.listdatafromparent which is pointing previous props. Rather than using this.props I would suggest consider props param of life cycle, means it should be like
componentWillReciveProps(props) {
// your code
Console.log(props.listdatafromparent);
}
The handleSubmit method is wrong... the console log is executed before the state is changed. You need to put it inside a callback function as a second parameter of setState.
this.setState({ passedValue: this.state.value }, () => {
console.log(this.state.passedValue);
});
Answers are:
1) Callback function should be used on setState, in order to do console.log after state is really updated.
In your case you call setState and setState is async function, which means that console.log won't wait until state is really updated.
Your code should be:
handleSubmit(event) {
this.setState({ passedValue: this.state.value },
() => console.log(this.state.passedValue));
event.preventDefault();
}
2) I would move data fetching out of componentWillReceiveProps(), since this lifecycle method will be deprecated from version 17 and it is fired on every render(). Try replacing with componentDidMount() or componentDidUpdate(). Maybe just this small change will solve your problem. If not pls post results and I will take a look again.

Using client side search in react

Search function is working perfectly fine in the console log but when I try to assign that value to rows which is a state. So I setState the rows inside the setState in searchHandler. I know I'm making a mistake but I don't know how to rectify it. OMIT THE UNDECLARED STATES, MINIFIED THE CODE TO WHAT'S NEEDED
function searchingFor(searchingTerm) {
return function(x){
// console.log("searching",x);
return x.name.toLowerCase().includes(searchingTerm.toLowerCase())|| false;
}
}
class Main extends React.Component{
componentWillMount(){
this.props.fetchTopicsTableContent(this.state.sortBy,'ASC',0,this.props.match.params.CategoryName).then(result=> (this.setState({rows:result.payload.data})))
this.props.countTableContent(this.props.match.params.CategoryName).then(result=>(this.setState({count:result.payload})));
}
constructor(props){
super(props);
this.state={
rows:"",
searchTerm:"",
items:""
}
}
onSubmit(values){
values.preventDefault();
}
onSearchHandler(e){
this.setState({searchTerm:e.target.value},()=>{
{this.state.rows.filter(searchingFor(this.state.searchTerm)).map(item=>{
console.log(item);
//this.setState({rows:item})
})}
})
}
render(){
return(
<div>
<h3>Topics</h3>
<hr/>
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<input type="text"
className="searchBar"
value={this.state.searchTerm}
onChange={this.onSearchHandler.bind(this)}
/>
</form>
</div>
</div>
)
}
Okay so lets start with binding your functions in the constructor, not in the markup, clean things up :P
Next, i'm not sure you understand how setting state works as your function goes against it's basic use. You are correctly setting the first state and using the callback (Because it takes time for state to actually be set) which is great. The callback function it's where it goes downhill.
Your mapping function is loading up several setState calls instantly, for each one console.log() will run successfully, but only one of the setStates will actually take effect. On top of that, even if it did work, your rows state will only have a single item. Lets try this:
onSearchHandler(e){
this.setState(prevState => {
return {
rows: prevState.rows.filter(searchingFor(e.target.value)),
searchTerm: e.target.value,
}
});
}
That will get you what I assume is the desired result... you should only ever do one setState at a time, unless you are waiting for the callback on each one, because you can't be sure each one will complete before the next.
Your logic is fine, but the code looks clumsy.I refactored the code so that only necessary logic is present and instead of bind use arrow functions.
Here, try this code on codeSandbox
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props){
super(props);
this.state = {
rows: ["asd", "bsd", "csd", "dsd", "esd"],
items: []
}
}
onSearchHandler = (e) => {
this.setState({ items: this.state.rows.filter(str => str.toLowerCase().includes(e.target.value.toLowerCase()))})
}
render(){
return (
<div>
<h3>Topics</h3>
<input type="text"
className="searchBar"
onChange={(e) => this.onSearchHandler(e)}/>
<p>{this.state.items.join('\n')}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

Unable to save input picture data in state variable React.js

i'm trying to learn how the image upload process works with react. i have an input that takes image data in and a handler that sets that information to a variable but i am doing something incorrect and not sure what it is.
I want to update picture in state with the picture information and leave the other items in state alone. When i do the below, and console.log after the state of picture stays null.
class Profile extends Component {
constructor() {
super();
this.state = {
userName: "",
userEmail: "",
picture: null
};
this.handleNewImage = this.handleNewImage.bind(this);
}
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
})
console.log(this.state.picture); //gives null still
}
render() {
return (
<input type='file' onChange={this.handleNewImage} />
);
}
}
This probably has nothing to do with images but rather an understanding of setState. React's setState is an async operation so you'd need to console log once the state update is complete:
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
}, () => {
console.log(this.state.picture);
});
}
More info here:
https://reactjs.org/docs/react-component.html#setstate
https://css-tricks.com/understanding-react-setstate/

Updating react component from sideBar menu

I have a simple react app that consists of 3 components:
1. SideBar that contains links
2. ItemList that contains data in a table
3. Wrapper that wraps both of them (I understood from some posts here that it is sometimes useful, as I want to update the ItemsList component after clicking on different links on the sideBar).
What I have working now:
In the main Wrapper component:
render() {
return (
<div>
<SideMenu handleClick={this.handleClick} />
<ItemsList url={this.state.currentUrl} />
</div>
);
}
as soon as the app starts, using componentDidMount() in ItemsList component, it fetches there the data, and display it. that works fine.
Problem is, when I click the links in the sideMenu component, I am changing the currentUrl in the state of the main wrapper, so then it will get re-rendered by the new url:
handleClick() {
this.setState({ currentUrl: 'here I put the new address to fetch from'});
}
but what gets fetched is data that is located in the previous url, not the one I had just changed it to.
Basically, right after I debug and check the state after I had changed it, the currentUrl stays the previous one, and then it re-renders the ItemList with the previous url.
my question is, how do I Change the content of the itemList, using this handleClick() method? Hopefully I will get some insights. thank you very much, I would appreciate your assistance.
the main wrapper Code:
class MainWrapper extends React.Component {
constructor() {
super();
this.state = {
currentUrl: 'current url to fetch from...',
data: []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ currentUrl: 'the new url ' });
}
render() {
return (
<div>
<SideMenu handleClick={this.handleClick} />
<ItemsList url={this.state.currentUrl} />
</div>
);
}
}
my Itemlist Component:
class ItemsList extends Component {
constructor(props) {
super(props);
this.state = { url: props.url, data: [] };
}
componentDidMount() {
return fetch(this.state.url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({ data: responseJson.data });
})
}
render() {
return (
displaying the table html tags..
}
</div>
)
}
}
You could the componentDidUpdate life cycle method in your itemList component. Each time the url changes, the listItem would presumably re-render based on what I understood from your question, so the componentDidUpdate method will fire. It is in this method where you can check for the new url and make the new request.
See more here.

Where do I call setState for redux values?

I'm pretty new to react native and async programming, and trying to understand how to "sync" redux state values and local state values.
For example, I have a text field "aboutMe" stored server side, and using mapStateToProps to place it into props:
const mapStateToProps = (state) => {
return { aboutMe: state.aboutMe };
}
In render, I have a TextInput I'm using so that the user can edit this field, and I would like to default to what is saved on the server side:
<TextInput
onChangeText={(aboutMe) => {
this.setState({aboutMe});
}}
value={this.state.aboutMe}
/>
Basically, somewhere I need to call
this.setState({ aboutMe: this.props.aboutMe });
Where is the right place to this? I was trying to use componentWillReceiveProps, but that lifecycle method is not called on constructor, so I would need to setState twice (in constructor and in componentWillReceiveProps).
Is there another way to do this? I feel like this is a pretty generic problem that a lot of react native developers have solved but I couldn't find a generally accepted way online.
Thanks!
Edit:
I have alot of TextInputs, so I have a separate button to call the action to save the variables:
<Button onPress={()=>{
this.props.saveUserInput(this.state.aboutMe,
this.state.name, this.state.address, ....}}>
<Text> Save changes </Text>
</Button>
From the comments, I understand that it's possible to call the save action onChangeText... but is that too much traffic back and forth? Would it be better to save all of the variables locally to state and then call a save for everything at once? Also, what if the user would like to "cancel" instead of save? The changes would have been already saved and we will not be able to discard changes?
1) If your component is a controlled component (you need state in it) and the request is asynchronous indeed you have to set the state in the componentWillReceiveProps like this:
class ExampleComp extends Component {
constructor(props) {
super(props);
this.state = {
aboutMe: ""
}
}
componentWillReceiveProps(nextProps) {
this.setState({
aboutMe: nextProps.aboutMe,
});
}
render() {
return (
<TextInput
onChangeText={(aboutMe) => {
this.setState({aboutMe});
}}
value={this.state.aboutMe}
/>
);
}
}
Keep in mind the key here is that the state must remain the single source of truth from now on.
2) The other option would be, you can wait until the request is finished in the parent component and then set the aboutMe in your constructor, this way you can avoid componentWillReceiveProps. For example:
class ParentComp extends Component {
render() {
return (
<div>
{this.props.aboutMe && <ExampleComp/>}
</div>
);
}
}
class ExampleComp extends Component {
constructor(props) {
super(props);
this.state = {
aboutMe: props.aboutMe
}
}
render() {
return (
<TextInput
onChangeText={(aboutMe) => {
this.setState({aboutMe});
}}
value={this.state.aboutMe}
/>
);
}
}
The downside of this is that the text input won't be shown until the request is finished.
Since you have edited your question, it is more clear what you want to achieve, so I want to address that.
You could keep the state of your controlled input elements in the component, then use the redux store to store persistent data and to populate the default values.
class Component extends React.Component {
constructor(props) {
super(props)
this.state = {
aboutMe: props.aboutMe,
... // other data
}
}
handleSubmit = (event) => {
event.preventDefault() // To prevent redirect
// Dispatch the save user input action
this.props.dispatch(saveUserInput(this.state))
}
render() {
return (
<form onSubmit={this.handleSubmit} />
<TextInput onTextChange={text => this.setState({...this.state, aboutMe: text}) />
... // input fields for other data
// Clicking this fill trigger the submit event for the form
<button type="submit">Save</button>
</form>
)
}
}

Resources