How to pass data between two react sibling components? - javascript

I have two components: which takes value from an input field. Second component is which I fetch api data. The problem is that I want to get the value from GetSearch as the value i search the API in Pexels.
I have tried to change my code multiple times. I just cant understand how it is supposed to be done, and how should I actually communicate together with my components.
import React from "react";
class GetSearch extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
var PassValue = React.CreateClass({
render: function() {
return (
<p>{this.state.value}</p>
);
},
});
return (
<form className="search-form">
<input
type="text"
placeholder="Search for images"
value={this.state.value}
onChange={this.handleChange}/>
<input type="submit"/>
</form>
);
}
}
export default GetSearch
import React, { Component } from 'react'
export default class Pexels extends Component {
componentDidMount(){
let query = "water"
const url = `https://api.pexels.com/v1/search?query=${query}e+query&per_page=15&page=1`
const api_key = "xxxxxxxxxxxx"
fetch(url, {
method: 'GET',
headers: new Headers({
'Authorization': api_key
})
})
.then(response => response.json())
.then(data => {
console.log(data)
})
.catch(error => console.error(error))
}
render() {
return (
<h1>Hello</h1>)
}
}
So as you can see now: Pexels sends a get request with the value of water: let query = "water", which works fine. But I need the value from
this.state.value in the GetSearch component

First, you need to create a parent class. Then You need to pass callback functions to the children as props. Here GetSearch component can be your child class. After you click search button your main class method will notify that change. Then create your logic as you want.
Follow this example code. thanks
Parent Component
var ParentComponent = React.createClass({
update: function() {
console.log("updated!");
},
render: function() {
<ChildComponent callBack={this.update} />
}
})
Child Component
var ChildComponent = React.createClass({
preupdate: function() {
console.log("pre update done!");
},
render: function() {
<button onClick={this.props.callback}>click to update parent</button>
}
})

You may need a store(just a function) to fetch url data rather than in a UI component Pexels.
In GetSearch invoke the store function with input as parameter and return a promise, and get data in callback.

Related

How to make a POST request with input text as data React

I am new to react and I am trying to make a POST request using text field data, can anyone help me with how to store that input and make a request after a button is pressed.
I attempted to use useRef() which allowed me to obtain the data however I was not able to store it as a data object to then persist.
Currently my data persists, however it persists an empty object and the state is not being updated.
If anyone can help, I will really appreciate that.
Below is my App.js class
import React, { useState, useEffect, useRef, Component } from 'react';
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists"
});
class App extends Component {
state = {
artists: [],
theArtistName: ""
}
constructor(props){
super(props);
this.getArtists()
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () =>{
let data = await api.get("/").then(({ data }) => data);
this.setState({artists: data}) //setting our artists to be the data we fetch
}
createArtist = async () =>{
let response = await api.post('/', {name: this.state.theArtistName})
console.log(response)
this.getArtists()
}
deleteArtist = async (id) =>{
let data = await api.delete('/${id}')
this.getArtists();
}
handleAddArtist = (event) =>{
event.preventDefault()
this.setState({
theArtistName: event.target.value
})
const data = this.state.theArtistName
console.log(data)
}
componentDidMount(){
this.createArtist()
}
render(){
// const {theArtistName} = this.state
return(
<>
<input type={Text} placeholder="Enter Artist Name" name="theArtistName"></input>
<button onClick={this.createArtist}>Add Artist</button>
{this.state.artists.map(artist => <h4 key={artist.id}>{artist.name}
<button onClick={() =>this.deleteArtist(artist.id)}>Delete artist</button></h4>)}
</>
)
}
}
export default App;
this.setState is an async function, it takes second argument as callback. This should solve your problem. i.e.
import React, { useState, useEffect, useRef, Component } from "react";
import axios from "axios";
const api = axios.create({
baseURL: "http://localhost:8080/artists",
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
artists: [],
theArtistName: "",
};
}
//calling this method will allow artist array to be populated everytime an event occurs, e.g POST, PUT, DELETE
getArtists = async () => {
let data = await api.get("/").then(({ data }) => data);
this.setState({ artists: data }); //setting our artists to be the data we fetch
};
createArtist = async () => {
let response = await api.post("/", { name: this.state.theArtistName });
console.log(response);
this.getArtists();
};
deleteArtist = async (id) => {
let data = await api.delete("/${id}");
this.getArtists();
};
handleAddArtist = (event) => {
event.preventDefault();
this.setState(
{
theArtistName: event.target.value,
},
() => {
this.createArtist();
}
);
};
componentDidMount() {
this.getArtists();
}
render() {
// const {theArtistName} = this.state
return (
<>
<input
type={Text}
placeholder="Enter Artist Name"
name="theArtistName"
></input>
<button onClick={this.handleAddArtist}>Add Artist</button>
{this.state.artists.map((artist) => (
<h4 key={artist.id}>
{artist.name}
<button onClick={() => this.deleteArtist(artist.id)}>
Delete artist
</button>
</h4>
))}
</>
);
}
}
export default App;
Let me know if it helps.
because react update state asynchronously so when you are invoking handleAddArtist function which update state the event might be gone so you need to store the value from the event in variable like this :
handleAddArtist = (event) =>{
event.preventDefault()
const {value} = e.target
this.setState({
theArtistName: value
})
}
and to check state update there is a lifecycle method called componentDidUpdate for class component and useEffect for functional component.
[edit]:
call this.createArtist() in componentDidUpdate like this :
componentDidUpdate(prevProps,prevState){
if(prevState.theArtistName!==this.state.theArtistName)
this.createArtist()
}
so the createArtist will fire only when theArtistName state change.
First of all, useRef is a hook only meant for function components and not for class components. For using Refs in class components use React.createRef().
Usually, HTML input elements maintain their own state. The usual way to access the value of an input element from a React component that renders it is to control the input element's state via this component by adding an onChange listener and a value attribute to the input element:
class App extends Component{
constructor(props) {
super(props);
this.state = {artistName: ""};
this.handleArtistNameChange = this.handleArtistNameChange.bind(this);
}
handleArtistNameChange(event) {
this.setState({artistName: event.target.value});
}
render(){
return (
<input
type="text"
value={this.state.artistName}
onChange={this.handleArtistNameChange}
/>
);
}
}
Whenever the value of the input element changes the App component will rerender with the most up-to-date value of the input in its state.
Here is a working example:
You can read more on using form elements in React here.

Is there a way of passing props from child to parent without onClick

I've seen examples that show how to pass props from a child to its parent with a onClick on onChange event on the child component, but am struggling to figure out how to pass props up passively.
What i'd like is to have the child component that performs a fetch operation and then passes the response up to the parent.
// PARENT component
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
homeLink: 'inital'
}
}
handleNamechange(newName) {
this.setState({
homeLink: newName
})
}
render() {
return(
<section>
<h1>{this.state.homeLink}</h1>
<GetUserComponent
changeLink={this.handleNamechange.bind(this)}
/>
</section>
)
}
}
export default App;
And the part I struggle with is sending the props up to the parent WITHOUT the onClick, and just pass the props once the fetch is complete
// CHILD Component
class GetUserComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
homeLink: 'xxxx'
}
}
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.setState(
{ username: data }
)
})
}
onChangeLink() {
this.props.changeLink(this.state.homeLink)
}
render() {
return (
<div>
<span onClick={this.onChangeLink.bind(this)}
>Change Header Link</span>
</div>
)
}
}
export default GetUserComponent;
I'm not sure if I'm doing this wrong, or whether this simply can't be done and you HAVE to use the click event, but either way would really appreciate your help.
You have to call the parents function:
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.props.changeLink(data);
})
}
It will then execute the handleNamechange function and update the parents state.
In your case, I think the parent must do the fetch, and give as props the result to the children.
If you really need the child fetch the data, you have to call the callback changeLink given as a props from the parent to the child as it :
componentDidMount() {
fetch('https://someapi/getusername', {
})
.then(function(response) {
return response.json()
})
.then((data) => {
this.setState(
{ username: data }
)
this.props.changeLink(data)
})
}

onChange event on input returns 'Cannot read property search of undefined'

I have two React components, one parent component and one child component. They are both in a separate file and the code looks like this:
import React, { Component } from 'react';
import SearchBar from './SearchBar';
import axios from "axios/index";
var processedQuery;
var url = 'https://some-url-api.com/q=';
class MainContent extends Component {
state = {
results: '',
query: ''
}
getData = () => {
axios.get(url + processedQuery)
.then(({ data }) => {
this.setState({
results: data
})
})
}
handleInputChange = () => {
processedQuery = this.search.value.split(' ').join('+');
this.setState({
query: processedQuery
}, () => {
this.getData();
}
)
}
render() {
return (
<div className="main-content">
<SearchBar handleInputChange={this.handleInputChange} />
</div>
);
}
}
export default MainContent;
And the child component:
import React, { Component } from 'react';
class SearchBar extends Component {
render() {
return (
<div className="main-content__search-bar">
<form>
<input
type="text"
placeholder="Search for..."
onChange={this.props.handleInputChange}
/>
</form>
</div>
);
}
}
export default SearchBar;
Everything worked fine while the getData and handleInputChange functions were inside of the child component. However, when i moved them to the parent component and try to type something in the input, I get this error:
Uncaught TypeError: Cannot read property 'value' of undefined
I went through the code multiple times and can't really find what I'm missing. Any help?
Your parent component should take the new value as a parameter:
handleInputChange = (event) => {
processedQuery = event.target.value.split(' ').join('+');
// ...
}
I'd argue that the child should actually pass the value in (not the event).
onChange={event => this.props.handleInputChange(event.target.value)}
And the parent can do:
handleInputChange = (searchTerm) => {
processedQuery = searchTerm.split(' ').join('+');
// ...
}

Update variable in React in class not extending component

I am trying to wrap my head around ReactJS and I am stumped with an issue where I want to be able to update the value of a local variable and return the updated value.
I've read about state and I've used that when working with React Components, however, this class is just defined as const and it doesn't extend React.Component.
Is there a different way I should be defining setting the variable?
Here is a simplified version of my code:
import React from 'react';
const WelcomeForm = ({welcome}) => {
var welcomeMsg = 'Test';
DynamicContentApi.loadDynamicContent('welcome_test').then((response) => {
// response.text has content
welcomeMsg = response.text;
}).catch(() => {
welcomeMsg = '';
});
return (
<p>{welcomeMsg}</p> // Returns 'Test'
);
};
export default WelcomeForm;
The easiest option here is to change your stateless component to a stateful component.
Stateless components are just JavaScript functions. They take in an
optional input, called prop.
Stateful components offer more features, and with more features comes more baggage. The primary reason to choose class components (stateful) over functional components (stateless) is that they can have state, that is what you want to update to re-render.
Here is what you can do:
class WelcomeForm extends React.Component {
state = {
welcomeMsg: ''
}
fetchFromApi() {
DynamicContentApi.loadDynamicContent("welcome_test")
.then(response => {
this.setState({welcomeMsg: response.text});
})
.catch((e) => console.log(e));
}
componentDidMount() {
fetchFromApi();
}
render() {
return (
<p>{welcomeMsg}</p>
);
}
};
If you want, for any reason, to keep your component stateless, you will have to put the loadDynamicContent() function on the Parent and pass the text to WelcomeForm as a prop. For example:
// Your WelcomeForm Component
const WelcomeForm = ({welcomeMsg}) => (
<p>{welcomeMsg}</p>
);
// Whatever it's Parent Component is
class Parent extends React.Component {
state = {
welcomeMsg: ''
}
fetchFromApi() {
DynamicContentApi.loadDynamicContent("welcome_test")
.then(response => {
// response.text has content
this.setState({welcomeMsg: response.text});
})
.catch((e) => console.log(e));
}
componentDidMount() {
fetchFromApi();
}
render() {
<WelcomeForm welcomeMsg={this.state.welcomeMsg} />
}
}
As suggested in the comments, you can pass the DynamicContentApi logic to outside:
import ReactDOM from 'react-dom'
DynamicContentApi.loadDynamicContent('welcome_test').then((response) => {
ReactDOM.render(<WelcomeForm data={response.text} />, document.getElementById('where you wanna render this'));
}).catch(() => {
console.log('error while fetching...');
});
And where you have your component:
import React from 'react';
export default class WelcomeForm extends React.Component {
render() {
return (
<p>{this.props.data}</p>
);
}
}

React defaultValue not working axios deliverd dynamic data

Hello im new in React and im trying to play a little with React but heres one point i dont understand.
at first, fetch with axios data who return my data, the following, then i try to put them into the input fields, value(and is readonly), defaultValue is better, now i have the problem, i see nothing, the value exists when i view with firebug, the strange thing is, when i add a unneed character the input get filled by my wanted but not by default.
The very strange thing is, when i put everything in a Array and does a map function over it i have the value
the json code
{"firma":"hallo","strasse":"musterweg 7","plz":"01662"}
the js code
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class Testx extends React.Component {
constructor(props) {
super(props);
this.state = {
data:[]
};
}
componentDidMount(){
var self = this;
axios.get('http://localhost/index.php')
.then(function (response) {
self.setState({ data: response.data});
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<input type="text" defaultValue={this.state.data.firma}/>
</div>
);
}
}
ReactDOM.render(<Testx/>, document.getElementById('hello'));
You need to wait until the data comes by showing something loading.
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class Testx extends React.Component {
constructor(props) {
super(props);
this.state = {
data:{}
};
}
componentDidMount(){
var self = this;
axios.get('http://localhost/index.php')
.then(function (response) {
self.setState({ data: response.data});
})
.catch(function (error) {
console.log(error);
});
}
render() {
const { data }= this.state;
if(data.firma) {
return (<div>
<input type="text" defaultValue={data.firma}/>
</div>);
}
return <div>loading...</div>;
}
}
ReactDOM.render(<Testx/>, document.getElementById('hello'));
Initially, your data state is in Array format. So this.state.data.firma doesnt work. Instead make it as empty object {}.
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class Testx extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
};
}
componentDidMount() {
var self = this;
axios.get('http://localhost/index.php')
.then(function (response) {
self.setState({ data: response.data});
})
.catch(function (error) {
console.log(error);
});
}
render() {
return <div>
<input type="text" defaultValue={this.state.data.firma}/>
</div>
}
}
ReactDOM.render(<Testx/>, document.getElementById('hello'));
The "code style" is outdated. Try to work with arrow functions which bind your functions, such as setState. Or bind your functions once in your constructor like this.myFunction = myFunction.bind(this) so you are able to access this. I already commented that this.state.data is declared as an array. Either change it to be an object or access an object by a specific index.
class Testx extends React.Component {
constructor(props) {
super(props);
this.state = {
data:{}
};
}
componentDidMount = () => { //Note the arrow function to bind this function
//Functions like componentDidMount are usually already bound
axios.get('http://localhost/index.php')
.then((response) => {
this.setState({ data: response.data});
})
.catch((error) => {
console.log(error);
});
}
render() {
return (
<div>
<input type="text" defaultValue={this.state.data.firma}/>
</div>
);
}
}
If your response is an array instead of an object, then try to access firma like this: this.state.data[index].firma
thanks all, special for the tips and tricks and how i can do thinks better, my questions is solved, big thanks to all for helping me in under 15 min happy
im now also found a way playing with https://facebook.github.io/react/docs/forms.html and set my state with
handleChange(event) {
var tmp = this.state.data;
tmp[event.target.id] = event.target.value
this.setState({data: tmp});
}
with modding my render
<input type="text" id="firma" value={this.state.data.firma} onChange={this.handleChange} />

Categories