How to communicate between two components in Reactjs - reactjs

I'm a newbie in React. Why can't my data pass to another component?? My idea is after the user select option tag, then it will load some audio files based off the selection. But it's not working. What did I miss?
Or is there a better to do it?
This is my dropdown component
import React from 'react';
import Playlist from './Playlist/Playlist.js';
class Dropdown extends React.Component {
constructor (props) {
super(props);
this.state = {value: "1"};
this.handleChange = this.handleChange.bind(this);
this.handleEnter = this.handleEnter.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
console.log("Hello");
console.log(event.target.value);
console.log(typeof(event.target.value));
}
handleEnter(event) {
alert("Clicked: " + this.state.value);
event.preventDefault();
}
render (){
return (
<form onSubmit={this.handleEnter} >
<label>
<div className="langueageSelection">
<Playlist langueageSelection={this.handleChange.bind(this)} />
<select value={this.state.value} onChange={this.handleChange} >
<option value=""> Please select a language </option>
<option value="1"> English (American) </option>
<option value="2"> Chinese (Mandarin) </option>
</select>
</div>
</label>
<input type="submit" value="Enter" className="EnterButton"/>
</form>
);
}
}
export default Dropdown;
This is my playlist component.
import React from 'react';
class Playlist extends React.Component {
handleChange({target}){
this.props.langueageSelection(target.value)
}
render() {
if (this.props.langueageSelection === "1"){
console.log("English");
}
else if(this.props.langueageSelection === "2"){
console.log("Chinese");
}
return (
<div> Hi</div>
);
}
}
export default Playlist;
This is my entry component:
import React from 'react';
import mind_zebra from '../../images/MindScribe-zebra.png';
import Dropdown from '../Dropdown/Dropdown.js';
class Entry extends React.Component {
state = { hideZebraPic : false};
onClickHandler = () => {
this.setState( prev => ({ hideZebraPic : !prev.hideZebraPic }));
};
render() {
if (this.state.hideZebraPic) {
return (
<div>
<Dropdown />
</div>
);
} else {
return (
<div>
<img src={mind_zebra} onClick={this.onClickHandler} className="MindZebraPic" alt="zebra"/>
</div>
);
}
}
}
Here's my structure of directory:
src
|
audio
- English audios
- Chinese audios
components
|
Dropdown
|
playlist
- playlist.js
dropdown.js
|
Entry
|
entry.js
|
Home
|
App.js

Or is there a better to do it?
Please take a look Single source of truth and its implementation by Redux or MobX

Maybe could you please specify what exactly does not work? Playlist does not receive selected language? Should it?
Because now, it looks like this:
<Playlist langueageSelection={this.handleChange.bind(this)} />
You pass function to Playlist, right? And as I understand, role of Playlist is to render some content, based on selections made in parent component, which is Dropdown? Why would you pass a function to the children component? An why would you decorate it with .bind(this)? You've bound "it" already in constructor (which is a "Dropdown" as I understand).
If you really need to get it working as it is:
In Dropdown, pass the "value" as a prop to child
<Playlist langueageSelection={this.state.value} />
In Playlist, I'm not quite sure if you need this function to be passed.
import React from 'react';
class Playlist extends React.Component {
render() {
if (this.props.langueageSelection === "1"){
console.log("English");
}
else if(this.props.langueageSelection === "2"){
console.log("Chinese");
}
return (
<div> Hi</div>
);
}
}
export default Playlist;
For further development:
I think you could get in touch with some programming patterns of React. Please make some research on "stateless components updating parent's state". I don't know your idea about whole application, but I feel that Playlist should not be a child of a Dropdown. I would propose something like this:
Dropdown and Playlist are siblings. Let's say that they parent App. App is stateful component. Dropdown and Playlist are stateless components. App passes function ChangeLanguage to the Dropdown. ChangeLanguage is bound to App. Function ChangeLanguage handles selection of lang in Dropdown, takes event and updates App state with event.target.value. Then App passes its state to Playlist as a prop.
I hope that I understood your issue right.

Related

Get value from input component and use it in another component in React

I'm new to React and have this simple code example where I simply need to take value from input and show the value back.
class App extends React.Component {
constructor(props){
super(props);
this.state = { word : ""};
this.onClick = this.onClick.bind(this);
}
onClick(e){
this.setState({word : /* how to obtain input value?? */});
}
render() {
return (
<>
<form>
<input type="text"/>
<button onClick={this.onClick}>Say it!</button>
</form>
<div>
{this.state.word}
</div>
</>
);
}
}
I know react want's me to use component state as a way to propagate information from parent component to it's children. What I don't know is how I should obtain state of a children to be used in another children.
I believe this should be doable in react in simple manner as the equivalent way of doing it using pure DOM or JQuery would also be very simple (one or two lines of code).
You can use createRef
import React, { createRef } from "react";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { word: "" };
this.onClick = this.onClick.bind(this);
}
textInput = createRef();
onClick(e) {
this.setState({ word: this.textInput.current.value });
}
render() {
return (
<div className="App">
<form>
<input ref={this.textInput} type="text" />
<button onClick={this.onClick} type="button">
Say it!
</button>
</form>
<div>{this.state.word}</div>
</div>
);
}
}
export default App;
check here CodeSandBox
A few things here. First I don't see children as you mention. Moreover, you say obtain state of a children to be used in another children. You have just one parent component here. Then, you are using a <form> which means a button inside will submit the values so you need the escape hatch of e.preventDefault(). Finally, if you must use a class based component instead of functional component, you don't need any more constructor and you can bind your functions with an arrow function. Here is a working example of what I presume you are asking: https://codesandbox.io/s/sleepy-minsky-giyhk

Passing checkbox list value from child to parent

I am creating search form with checkboxes and call api URL. I need advise on how to pass selected checkboxes value from child component back to parent component and update the selected value string state. Thanks.
//CheckboxTest.js
import React,{Component} from 'react';
const data=[
{checked:false, value:'location A'},
{checked:false, value:'location B'}
];
class CheckboxTest extends Component{
state={items:[]};
onCheckChange=(idx)=>{
const items = this.state.items.concat();
items[idx].checked = !items[idx].checked;
this.setState({items});
this.props.onChange(this.state.items);
};
render(){
return (
<div>{data.map(item=>(<div>
<input type="checkbox" checked={item.checked} onChange= {item.onCheckChange} />
{ item.value }
</div>))}</div>
);
}
}
export default CheckboxTest;
//App.js
import CheckboxTest from './CheckboxTest';
class App extends Component {
state={
items:[],
itemchecked
};
componentDidMount(){
//call api
this.runSearch({items});
}
//handle checkbox changes
handleChange(id) {
//how to set items state here ???
this.runSearch(id);
}
render(){
return (
<div className="App">
<form>
<CheckboxTest onChange={this.handleChange} checked={this.itemchecked} />
</form>
</div>
);
}
}
export default App;
In React there is a one-way data flow. All data flows from parents to children. As such, the parent has to own the function (as you have done with your handleChange-function) and the child has to run that function. This last part you have not done.
You need to use the props of the child to access the function you have passed down from the parent. Read more here:
https://reactjs.org/docs/faq-functions.html

Passing state-based data between pages in React

I am trying to create a multiple page form in React, and I have the basic wireframe set up. I am trying to export a user's Name from one page to the next, but the user will change depending on who has logged in. I've been in google purgatory for a while trying figure out how to grab a specific state-based value out of a component to be available on another page. In my code below, I'm exporting the whole component to render on the App.js page. However, I'd also like to grab just the {userName} to render within another component.
import React, { Component } from 'react'
class Intro extends Component {
state = { userName: ''}
handleChange = (event) => this.setState({ userName: event.target.value })
render() {
const { userName } = this.state
return (
<div id='intro'>
<form>
<FieldGroup
id='nameArea'
value={this.state.value}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
export default Intro
To put it simply, you can't. This is where tools like redux come into play. Here's an example using React's new context API:
const UserContext = React.createContext('');
class Intro extends Component {
handleChange = (event) => {
this.props.updateUserName(event.target.value);
}
render() {
const { userName } = this.props
return (
<div id='intro'>
<form>
<input
id='nameArea'
value={userName}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
// only doing this to shield end-users from the
// implementation detail of "context"
const UserConsumer = UserContext.Consumer
class App extends React.Component {
state = { userName: '' }
render() {
return (
<UserContext.Provider value={this.state.userName}>
<React.Fragment>
<Intro userName={this.state.userName} updateUserName={(userName) => this.setState({userName})} />
<UserConsumer>
{user => <div>Username: {JSON.stringify(user)}</div>}
</UserConsumer>
</React.Fragment>
</UserContext.Provider>
)
}
}
See my updated codesandbox here.
Most of the time, when you need data on another component, the solution is store the date higher in your component.
As the others already said, the most easy way is trying to pass your state via props from your higher order component to the childs.
Another approach would be to use Redux for your state management. This gives you one global state store accessible from any component.
Third you can try to use the react context api.

React component re renders to it's initial state automatically

I am making a simple react test application.
What is the application: It shows names of projects and has an option to add a new project. Each project has title and category.
This is how it looks
Problem: When I try to add a new project by entering the title and then clicking on submit button, the new project name appears in the projects for a fraction of seconds and then disappears. The project list gets back to the initial state which is three projects (which are shown below)
This is the code
App.js
import React, { Component } from 'react';
import './App.css';
import Projects from "./Components/Projects"
import AddProjects from './Components/AddProject'
class App extends Component {
constructor(){
super();
this.state = {
projects:[]
}
}
componentWillMount(){
this.setState({projects:[
{
title: "Trigger",
category: "Web App"
},
{
title: "Trigger",
category: "Web App"
},
{
title: "Trigger",
category: "Web App"
}
]})
}
handleAddProject(project){
let projects = this.state.projects;
projects.push(project)
this.setState({projects:projects})
console.log(this.state.projects)
}
render() {
return (
<div className="App">
My Project
<AddProjects addProject={this.handleAddProject.bind(this)}/>
<Projects projects={this.state.projects}/>
</div>
);
}
}
export default App;
Projects.js
import React, { Component } from 'react';
import Projectitem from './ProjectItem'
class Projects extends Component {
render() {
let projectItems
if(this.props.projects){
projectItems = this.props.projects.map(project =>{
return (
<Projectitem project={project}/>
);
})
}
else{
console.log("hello")
}
return (
<div>
This is a list of objects
{projectItems}
</div>
);
}
}
export default Projects
ProjectItem.js
import React, { Component } from 'react';
class ProjectItem extends Component {
render() {
return (
<li>
{this.props.project.title}:{this.props.project.category}
</li>
);
}
}
export default ProjectItem
AddProject.js
import React, { Component } from 'react';
var categories = ["Web dev", "Mobile dev", "websiite"]
class AddProject extends Component {
constructor(){
super();
this.state = {
newProject:{}
}
}
handleSubmit(){
this.setState({newProject:{
title:this.refs.title.value,
category:this.refs.category.value
}}, function () {
this.props.addProject(this.state.newProject)
})
}
render() {
var categoryOptions = categories.map(category=>{
return <option key={category} value={category}>{category}</option>
})
return (
<div>
Add Project <br/>
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>title</label><br/>
<input type="text" ref="title"/>
</div>
<div>
<label>Category</label><br/>
<select ref="category">
{categoryOptions}
</select>
</div>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default AddProject
Add event.preventDefault() to your handleSubmit() method
handleSubmit(event) {
event.preventDefault();
alert('A name was submitted: ' + this.state.value);
}
Without it your page will refresh (as it does by default when a form is submitted)
You are editting the state directly, that's a big no-no in React.
Change your handler function to something like this:
handleAddProject(project){
this.setState((prevState) => {
return { projects: [...prevState.projects, project] }
})
}
The setState function can be called using a 'callback', that receives the state previous to its call, and lets you modify it. The important part is not mutating the state directly (as your projects.push(project) line is doing).
Other option, that's more like what you are already trying to do, is copying the current state before mutating it:
handleAddProject(project){
let projects = this.state.projects.slice(); // notice the slice here will return
// a copy of 'projects', and then
// you modify it
projects.push(project)
this.setState({projects:projects})
console.log(this.state.projects)
}
Also, keep in mind, setState is called asynchronously, so the console.log(this.state.projects) call may show the old state yet, as setState may not have been called yet.
You are using a Form tag which is for submit information to a web server if you use the input type="submit" it will always reload the page and initial everithig as the first time loaded. If you do as #Galupuf it will work
because the event.preventDefault(); will not allow the browser to reload and it will not initial everyting as first time loaded.
My answer is if you are not sending any data to any sever or working with a server at all change the form tag for div the input type="submit" for<button onClick={()=>this.handleSubmit()}>Send</button>
<div>
<div>
<label>title</label><br/>
<input type="text" ref="title"/>
</div>
<div>
<label>Category</label><br/>
<select ref="category">
{categoryOptions}
</select>
</div>
<button onClick={()=>this.handleSubmit()}>Submit</button>
</div>
other suggestion work the modified value of the state like #cfraser suggest like that you will have immutable data

Conditional List in ReactJS Based On State

I am pretty new to React JS and I am just wondering how I can filter what my component renders based on the state of my prop.
So I have the following component that allows me to select a certain brand and store it as a prop:
var React = require('react');
import Select from 'react-select';
class VehicleSelect extends React.Component {
constructor(props) {
super(props);
this.state = { brandSelect: ""};
}
_onChange(value) {
//console.log(value) - just to see what we recive from <Select />
this.setState({brandSelect: value}, () => {
console.log(this.state.brandSelect);
});
}
render() {
var options = [
{ value: 'Volkswagen', label: 'Volkswagen' },
{ value: 'SEAT', label: 'SEAT' },
{ value: 'SKODA', label: 'SKODA' }
];
return (
<Select
name="form-field-name"
value={this.state.brandSelect}
options={options}
placeholder="Select a brand"
searchable={false}
onChange={this._onChange.bind(this)}
/>
)
}
};
// Export our component
export default VehicleSelect;
This component works as expected however I am having issues when it comes to taking the brandSelect prop and conditionally deciding what my component should render.
Here is my details component:
var React = require('react');
import { If, Then, Else } from 'react-if';
import VehicleSelect from './vehicle-select.js';
// Vehicle Description Component
const VehicleDetail = (props) => {
return (
<If condition={ this.state.brandSelect === props.vehicle.brand.name }>
<div className="col-flex-md-3 col-flex-sm-4 col-flex-xs-6 col-flex-media-query">
<div className="vehicle-container">
<img src={"https://s3-eu-west-1.amazonaws.com/pulman-vw-images/uploads/images/thumbnails/" + props.vehicle.offers[0].image.name} />
<h4 className="vehicle-title">
{props.vehicle.model.name}
</h4>
<div className="row-flex">
<div className="col-flex-xs-12 btn-container">
Learn More
</div>
</div>
</div>
</div>
</If>
);
};
// Export our component
export default VehicleDetail;
As you can see it constructs a HTML container with data. I have also added a conditional statement (react-if on GitHub) to try and render data that matches the option that was selected in my VehicleSelect component however this doesn't seem to work. I get the following error:
Warning: There is an internal error in the React performance measurement code. Did not expect componentDidUpdate timer to start while render timer is still in progress for another instance.
Here is my component that iterates over my VehicleDetail component and supplies data to it:
var React = require('react');
// Vehicle List Componen
import VehicleDetail from './vehicle-detail.js';
// Create our component
const VehicleList = (props) => {
// Just add props.vehicle to access API data instead of static
const RenderedVehicles = props.vehicles.map(vehicle =>
<VehicleDetail key={vehicle.slug} vehicle={vehicle} />
);
return (
<div className="row-flex center-xs">
{RenderedVehicles}
</div>
);
};
// Export our component
export default VehicleList;
So my question is where am I going wrong? As I am new to ReactJS I am unsure how I can render components based on the state that has been selected. All I am trying to do is show data that matches the brandSelect prop in my VehicleSelect component. If the brandSelect prop is equals "" then I would like to render all of the data that is mapped in my VehicleList component.
Thanks
You cannot use this.state.brandSelect in the Details component
Since your details component is setup and brandselect is present in the parent component too

Resources