Component is not receiving the value from another page - javascript

I am new to react and I am having difficulty receiving value from another component. So what I am trying to do is that I am taking in an input value which is the stock ticker from the user in serachBar.js and passing the the ticker value to salesModel.js where the stock data will be displayed in a table format. However, before displaying the data I am sending the value to StockData.js where the API retrives the information based on the user input and sends the data back to the salesModel.js file to be displayed. However, for reason I cant pass the value from the searchBar.js file tosalesModel.jsfile. I have places the code the below for both components and also thank you in advance for any advice I would really appreciate it.
Searchbar.js
class SearchBar extends React.Component{
constructor(props){
super(props);
this.state = {
inputTicker:''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// handleSubmit (event) {
handleSubmit = event => {
event.preventDefault();
const { inputTicker } = this.state;
console.log("Current value?", inputTicker);
}
handleChange = event => {
// handleChange(event) {
// event.preventDefault();
console.log("Taking in value:",event.target.value);
this.setState({
inputTicker: event.target.value
});
// console.log("State:", JSON.parse(this.state.inputTicker));
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit} >
<div className="search-container">
<span className="search-icon-btn" >
<button className="search-icon-btn"
value="Submit"
type="submit"
>
<i className="fa fa-search" ></i>
</button>
</span>
<div className="search-input">
<input
type="text"
className="search-bar"
placeholder="Search ticker symbol..."
value={this.state.inputTicker}
onChange= {this.handleChange}
/>
</div>
</div>
</form>
</div>
)
}
}
export default SearchBar;
salesModel.js
class salesModel extends Component{
// function salesModel (props) {
constructor(props){
super(props);
this.state = {
ticker:''
}
}
componentDidMount() {
console.log("passed the value:",this.props.inputTicker);
}
render(){
return(
<div className="salesModel" >
<h1>Price to Sales Model (PS)</h1>
<table className="table mt-5">
<tbody>
<tr>
<th>Ticker</th>
<th>Company</th>
<th>Price</th>
<th>PE Ratio</th>
<th>Market Cap</th>
<th>Shares Outstanding</th>
<th>Beta</th>
</tr>
</tbody>
<tbody>
{/* <SearchBar handleSubmit={(input)=> this.setState({ticker: input})}/> */}
{/* <StockData ticker = "AAPL" /> */}
{/* <StockData ticker = {this.props.inputTicker} /> */}
</tbody>
</table>
</div>
);
}
}
export default salesModel;
I have tried calling the <SearchBar handleSubmit={(input)=> this.setState({ticker: input})}/> and setting the value as to ticker but for some reason that didn't work as well. I have tried to use the props.inputTicker from the searchBar.js file but for some reason it would give me an error Uncaught (in promise) SyntaxError: Unexpected token U in JSON at position 0. Honestly I have been stuck at this problem for sometime any help would be greatly appreciative thank you.

So what you need is a parent component that takes care of the state for both of them.
In this example the App.js is the parent and the SearchBar.jsand SalesModal.js are the children
Here is an codesandbox example
// App.js (parent component)
import React, { useState } from "react";
import SearchBar from "./SearchBar";
import SalesModal from "./SalesModal";
const App = () => {
const [searchBarText, setSearchBarText] = useState("");
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<SearchBar
searchBarText={searchBarText}
setSearchBarText={setSearchBarText}
/>
<SalesModal searchBarText={searchBarText} />
</div>
);
};
export default App;
// SearchBar.js (child component)
import React from "react";
const SearchBar = ({ searchBarText, setSearchBarText }) => {
return (
<input
type="text"
value={searchBarText}
onChange={(e) => setSearchBarText(e.target.value)}
/>
);
};
export default SearchBar;
// SalesModal.js (child component)
import React from "react";
const SalesModal = ({ searchBarText }) => {
return <div> searchBar Text: {searchBarText}</div>;
};
export default SalesModal;

Related

React holds state of no more than one array element

I've come to a halt making this covid19 app where I can see a list of countries on the left side of the screen with the option of adding any number of countries to the right side of the screen, which displays more covid data of the added country. I'm also kinda new to React.
Problem is, when I click the add button the added state is updated, and it displays that added country on the right side of the screen. But, when I try adding another country I get an error. I believe the error is somewhere around when I try to setState({ state }) in the addCountry method from within App.js.
In other words, the 'added' state is only letting itself hold no more than one array element. Help much much much appreciated. I posted all the code.
index.js
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
App.js
import CountryList from "./components/CountryList.js";
import Find from "./components/Find.js";
import Added from "./components/Added.js";
class App extends Component {
constructor() {
super();
this.state = {
countries: [],
inputbox: [],
added: [],
};
}
// Arrow functions capture "this" when they are defined, while standard functions do when they are executed.
// Thus, no need for the bind method. Awesome.
handleChange = (e) =>
this.setState({
inputbox: e.target.value,
});
getCountryData = async (slug) => {
const resp = await fetch(`https://api.covid19api.com/live/country/${slug}`);
var addedData = await resp.json();
// Api returns most days of covid, per country, that it tracks
// Thus, we want the last tracked day of a country
addedData = addedData[addedData.length - 1];
return addedData;
};
// Add a country to the added state
// Call when user clicks button associated with their desired country
addCountry = async (btnId) => {
const { countries, added } = this.state;
var addedData = await this.getCountryData(btnId);
countries.map((country) => {
// If the button ID is equal to the current country in the loops' Slug
if (btnId == country.Slug) {
try {
added.push([
{
addedCountry: addedData.Country,
confirmedTotal: addedData.Confirmed,
deathsTotal: addedData.Deaths,
recoveredTotal: addedData.Recovered,
activeTotal: addedData.Active,
},
]);
// (bug) IT IS PUSHING, BUT ITS NOT SETTING THE STATE!
// ITS ONLY LETTING ME KEEP ONE ITEM IN THE STATE
this.setState({ added });
console.log(added);
} catch (error) {
alert(`Sorry, country data not available for ${country.Country}`);
return;
}
}
});
};
removeCountry = (btnId) => {
const { added } = this.state;
added.map((added, index) => {
//console.log(added[index].addedCountry);
if (btnId == added[index].addedCountry) {
added.splice(index, 1);
this.setState({ added: added });
} else {
console.log("not removed");
return;
}
});
};
// Mount-on lifecycle method
async componentDidMount() {
const resp = await fetch("https://api.covid19api.com/countries");
const countries = await resp.json(); // parsed response
this.setState({ countries }); // set state to parsed response
}
render() {
// Filter out countries depending on what state the inputbox is in
const { countries, inputbox } = this.state;
const filtered = countries.filter((country) =>
country.Country.includes(inputbox)
);
return (
<div className="App Container">
<Find
placeholder="Type to find a country of interest..."
handleChange={this.handleChange}
/>
<div className="row">
<CountryList countries={filtered} addCountry={this.addCountry} />
<Added added={this.state.added} removeCountry={this.removeCountry} />
</div>
</div>
);
}
}
export default App;
Added.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import AddedCountry from "./AddedCountry.js";
class Added extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="col-md-6">
<Table>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Country</th>
<th scope="col">Active</th>
<th scope="col">Confirmed Total</th>
<th scope="col">Recovered</th>
<th scope="col">Deaths</th>
<th scope="col">Action</th>
</tr>
</thead>
{this.props.added.map((added, index) => (
<AddedCountry
added={added[index]}
removeCountry={this.props.removeCountry}
/>
))}
</Table>
</div>
);
}
}
export default Added;
AddedCountry.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class AddedCountry extends Component {
constructor(props) {
super(props);
}
render() {
return (
<tbody>
<tr>
<td></td>
<td>{this.props.added.addedCountry}</td>
<td>{this.props.added.activeTotal}</td>
<td>{this.props.added.confirmedTotal}</td>
<td>{this.props.added.recoveredTotal}</td>
<td>{this.props.added.deathsTotal}</td>
<td>
{
<Button
onClick={() =>
this.props.removeCountry(
document.getElementById(this.props.added.addedCountry).id
)
}
id={this.props.added.addedCountry}
type="submit"
color="danger"
size="sm"
>
Remove
</Button>
}
</td>
</tr>
</tbody>
);
}
}
export default AddedCountry;
CountryList.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
import Country from "./Country.js";
class CountryList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="col-md-6">
<Table>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Country</th>
<th scope="col">Actions</th>
</tr>
</thead>
{
// Each country is a component
// Function will display all countries as the Map function loops through them
this.props.countries.map((country) => (
<Country countries={country} addCountry={this.props.addCountry} />
))
}
</Table>
</div>
);
}
}
export default CountryList;
Country.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class Country extends Component {
constructor(props) {
super(props);
}
render() {
return (
<tbody>
<tr>
<td></td>
<td>{this.props.countries.Country}</td>
<td>
{
<Button
onClick={() =>
this.props.addCountry(
document.getElementById(this.props.countries.Slug).id
)
}
id={this.props.countries.Slug}
type="submit"
color="success"
size="sm"
>
Add
</Button>
}
</td>
</tr>
</tbody>
);
}
}
export default Country;
Find.js
import React, { Component } from "react";
import { Table, Form, Input, Button } from "reactstrap";
class Find extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="Find container">
<br />
<Form>
<div className="form-row">
<div className="form-group col-md-6">
<h3>Find a Country</h3>
<Input
type="text"
className="form-control"
id="country"
placeholder={this.props.placeholder}
onChange={this.props.handleChange}
></Input>
</div>
</div>
</Form>
</div>
);
}
}
export default Find;
I haven't pored over all that code, but focusing right where you think the issue is it is obvious you are mutating your state object by pushing directly into the added array.
Solution
Don't mutate state!
Since it seems you only want to add a single new "add" and only when the button's btnId matches a country's slug, and the btnId can only ever be a valid value from the mapped countries array, I think this can be greatly simplified.
addCountry = async (btnId) => {
const addedData = await this.getCountryData(btnId);
if (addedData) {
this.setState(prevState => ({
added: prevState.added.concat({ // <-- concat creates a new array reference
addedCountry: addedData.Country,
confirmedTotal: addedData.Confirmed,
deathsTotal: addedData.Deaths,
recoveredTotal: addedData.Recovered,
activeTotal: addedData.Active,
}),
}));
} else {
alert(`Sorry, country data not available for ${country.Country}`);
}
};
Similarly the removeCountry handler is mis-using the array mapping function and mutating the added state. Array.prototype.filter is the idiomatic way to remove an element from an array and return the new array reference.
removeCountry = (btnId) => {
this.setState(prevState => ({
added: prevState.added.filter(el => el.addedCountry !== btnId),
}));
};
Additional Issues & Suggestions
Added.js
If you maintain the added array as a flat array (not an array of arrays) then it's trivial to map the values.
{this.props.added.map((added) => (
<AddedCountry
key={added}
added={added}
removeCountry={this.props.removeCountry}
/>
))}
Country.js & AddedCountry.js
I don't see any reason to query the DOM for the button id when you are literally right there and can enclose the country slug in the onClick callback.
<Button
onClick={() => this.props.addCountry(this.props.countries.Slug)}
id={this.props.countries.Slug}
type="submit"
color="success"
size="sm"
>
Add
</Button>
<Button
onClick={() => this.props.removeCountry(this.props.added.addedCountry)}
id={this.props.added.addedCountry}
type="submit"
color="danger"
size="sm"
>
Remove
</Button>
App.js
This may or may not matter, but it is often the case to do case-insensitive search/filtering of data. This is to ensure something like "France" still matching a user's search input of "france".
const filtered = countries.filter((country) =>
country.Country.toLowerCase().includes(inputbox.toLowerCase())
);

ReactJS: Dynamically add a component on click

I have a menu button that when pressed has to add a new component. It seems to work (if I manually call the function to add the components they are shown). The problem is that if I click the button they are not shown, and I suppose because I should use setState to redraw them. I am not sure how to call the setState of another component within another function/component.
This is my index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Menu from './Menu';
import * as serviceWorker from './serviceWorker';
import Blocks from './Block.js';
ReactDOM.render(
<div className="Main-container">
<Menu />
<Blocks />
</div>
, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();
Then I have the Menu.js
import React from 'react';
import './Menu.css';
import {blocksHandler} from './Block.js';
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd(event) {
blocksHandler.add('lol');
console.log(blocksHandler.render());
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={this.handleAdd}>Add block</button>
</header>
</div>
);
}
}
export default Menu;
And finally the Block.js
import React from 'react';
import './Block.css';
// this function adds components to an array and returns them
let blocksHandler = (function() {
let blocks = [];
return {
add: function(block) {
blocks.push(block);
},
render: function() {
return blocks;
}
}
})();
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
class Blocks extends React.Component {
render() {
return (
<div>
{blocksHandler.render().map(i => (
<Block key={i} />
))}
</div>
)
}
}
export default Blocks;
export {blocksHandler};
I am a React complete beginner so I'm not even sure my approach is correct. Thank you for any help you can provide.
Below I've knocked up a really simple Parent / Child type setup,..
The Parent is responsible for rendering the Buttons, I just used a simple numbered array here. When you click any of the buttons, it calls the setState in the Parent, and this in turns causes the Parent to re-render it's Children.
Note: I've also used React Hooks to do this, I just find them more
natural and easier to use. You can use Classes, the same principle
applies.
const {useState} = React;
function Child(props) {
const {caption} = props;
const {lines, setLines} = props.pstate;
return <button onClick={() => {
setLines([...lines, lines.length]);
}}>
{caption}
</button>;
}
function Parent(props) {
const [lines, setLines] = useState([0]);
return lines.map(m => <Child key={m} caption={`Click ${m}`} pstate={{lines, setLines}}/>);
}
ReactDOM.render(<React.Fragment>
<Parent/>
</React.Fragment>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="mount"></div>
Instead of creating blocksHandlers as a separate function ,you can have it nside the Menu.js like as follows
*
class Block extends React.Component {
constructor(props) {
super(props);
this.state = {
title: '',
content: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.title);
event.preventDefault();
}
render() {
return (
<div className="Block-container">
<form onSubmit={this.handleSubmit}>
<div className="Block-title">
<label>
Block title:
<input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<div className="Block-content">
<label>
Block content:
<input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
</label>
</div>
<input type="submit" value="Save" />
</form>
</div>
);
}
}
Menu.js
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',blocksArray:[]};
this.handleAdd = this.handleAdd.bind(this);
}
handleAdd() {
this.setState({
blocksArray:this.state.blocksArray.push(block)
})
}
renderBlocks = ()=>{
this.state.blocksArray.map(block=> <Block/>)
}
render() {
return (
<div className="Menu">
<header className="Menu-header">
<button className="Menu-button" onClick={()=>this.handleAdd()}>Add block</button>
</header>
{this.renderBlocks()}
</div>
);
}
}
export default Menu;

Conditional rendering based on user input

I've just started learning React and am struggling with conditional rendering. I want to render components based on form input but i'm not sure what needs to be done or where it needs to be executed.
I have imported my Form component which has the input I want to use and have another component like this:
import React, {Component} from 'react';
import Form from './Form';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class CardContainer extends Component {
render(){
return (
<div>
<CardOne />
<CardTwo />
<CardThree />
</div>
)
}
}
I basically want to be able to show certain Cards if the value of the input is greater than X when the form is submitted, but I don't know how to target an imported component.
This is my Form component:
export default class Form extends Component {
state = {
number: ''
};
change = (e) => {
this.setState({
[e.target.name]: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
this.setState({
number: ''
})
};
render(){
return (
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.number}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
)
}
}
Any help will be massively appreciated!
I have redesigned your Form component , Below is my code. . Let me know if u faced any issues on that .
import React, { Component } from 'react';
import CardOne from './CardOne';
import CardTwo from './CardTwo';
import CardThree from './CardThree';
export default class Form extends Component {
state = {
number: '',
showOne:true,
showTwo:false,
showThree:false,
userInputValue:''
};
change = (e) => {
this.setState({
userInputValue: e.target.value
});
};
onSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
if (this.state.userInputValue > 10 && this.state.userInputValue <20 ){
this.setState({
showTwo: true,
})
}
if (this.state.userInputValue > 20 && this.state.userInputValue < 30) {
this.setState({
showThree: true,
})
}
};
render() {
return (
<div>
<form>
<label>Number</label>
<input
type="number"
name="number"
placeholder="Number"
value={this.state.userInputValue}
onChange={e => this.change(e)} />
<button onClick={e => this.onSubmit(e)}>Submit</button>
</form>
<div>
{this.state.showOne ?
<CardOne />
:
<div></div>
}
{this.state.showTwo ?
<CardTwo />
:
<div></div>
}
{this.state.showThree ?
<CardThree />
:
<div></div>
}
</div>
</div>
)
}
}
// What i wrote above is your base functionality . You reedit the condition depends on ur requirement .
This is what I came up with following your logic of card rendering. I did not change Form coponent but rather worked on the Container
export default class CardContainer extends Component {
constructor(props) {
super(props);
state = {
number: 0,
}
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit=(number)=>{
this.setState({ number: number });
}
render(){
let i=Math.floor(this.state.number/10)
return (
<div>
<Form onSubmit={() => this.onFormSubmit(number)}
[<CardOne />, <CardTwo />, <CardThree/>].slice(0,i).map(card =>
{card}
)
</div>
)
}
}
I would use render prop for this kind of problem. You can research more about render props but basically your CardContainer component will not render those cards component statically as it is. It will return props.children instead.
And then you will have a function (i.e function TestX) that will have a conditional to check what the value of X is. This is the function that will return either , , based on what X is. The function TestX will receive props from CardContainer, including the value of X that is read from the state.
So I will just use CardContainer component with as its child.

React Help Needed - Components not updating when index increases

I cannot get the component displayed to update when the index increases. I am able to console the proper component now but because the onClick is below the component that needs to update, it isn't changing. Can someone help me fix my code? i think I am close but cannot figure it out for the life of me.
This sign up page is where I would like to update the component. Essentially I want to display each component in the array once the next button is clicked. Currently the function console logs everything as I want it to, it's just a matter of getting it to appear in the
it is returning an error "cannot read property 'count' of null":
import React from 'react';
import Q1Name from './questions/Q1Name';
import Q2Birthday from './questions/Q2Birthday';
import Q3City from './questions/Q3City';
import Q4YouReady from './questions/Q4YouReady';
import Q5Setting from './questions/Q5Setting';
import Q6Length from './questions/Q6Length';
import Q7Email from './questions/Q7Email';
class SignUpPage extends React.Component {
constructor(props) {
super(props);
this.state = {
i: 0
}
}
_handleClick() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
// handleIncrement() {
// this.setState({ count: this.state.count + 1});
// }}
render() {
const components = [Q1Name, Q2Birthday, Q3City, Q4YouReady, Q5Setting, Q6Length, Q7Email];
const componentsToRender = components.map((Component, i) => (
<Component key={i} />
));
return (
<div className = "container-fluid signup-page">
<div className = "question-box">
{componentsToRender[this.state.i]}
<button type="submit" className="btn btn-custom btn-lg" onClick={() => this._handleClick}>Next Question!</button>
</div>
</div>
);
}
}
export default SignUpPage;
There are a few component types I am bringing in, age, birthday, email, and a few button clicks, etc.
import React from 'react';
class Q1Name extends React.Component {
handleSubmit(event) {
event.preventDefault();
this.props.onNext();
}
render() {
return (
<div className="questions q1" style={this.props.style}>
<h1 id="question-h1">What is your name?</h1>
<form>
<div className="form-group">
<input type="name" className="form-control text-form custom-form" id="nameInput" aria-describedby="name" placeholder="" />
</div>
{/* <button type="submit" className="btn btn-custom btn-lg" onSubmit={this.handleSubmit}>Next Question!</button> */}
</form>
</div>
);
}
}
export default Q1Name;
Here is an example of the button option component:
import React from 'react';
class Q5Setting extends React.Component {
render() {
return (
<div className="questions">
<h1 id="question-h1">What is your ideal setting?</h1>
<button type="button" className="btn btn-custom-select btn-lg">Take me to the beach!</button>
<button type="button" className="btn btn-custom-select btn-lg">Anywhere outdoors!</button>
<button type="button" className="btn btn-custom-select btn-lg">All about the city!</button>
</div>
);
}
}
export default Q5Setting;
Any help in figuring this out would be greatly appreciated!!
In your constructor initialise state
constructor(props) {
super(props)
this.state = { i: 0 }
}
Write helper method handleClick
_handleClick() {
if(this.state.i < components.length) this.setState({ i : this.state.i + 1});
}
Now reference componentsToRender using i in state
`componentsToRender[this.state.i]
Don't forget to call your helper function on click.
onClick = {() => this._handleClick()}
The idea is your app will only re-render when your state object changes. Follow that rule for your components you wish to re-erender on the fry.

Not getting desired values in input fields React redux or ReactJS

import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedZonesEdit} from '../../actions/';
class InfoRow extends Component {
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
render() {
return (
<tr>
<td>
{this.props.zone}
</td>
<td>{this.props.zoneValue}
<input type="text"
className="form-control"
defaultValue={this.props.zoneValue}
value={this.props.name}
name={this.props.zone}
onChange={this.handleInputChange}
/>
</td>
</tr>
)
}
}
class ZoneDetailsEdit extends Component {
render() {
const rows = [];
let a = this.props.ezn;
Object.keys(this.props.ezn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow zone={keyName} zoneValue={a[keyName].toString()} key={keyIndex}/>)
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="row px-1" >
<div className="px-2">
<button className="btn btn-sm btn-info">Save</button>
</div></div>
</div>
)
}
}
class ZoneDetailEditComponent extends Component {
componentWillMount = () => {
this.props.fetchedZonesEdit(this.props.location.query.id);
};
render() {
return (
<div className="container px-3 mr-3">
<div className="row">
<div className="col-md-6 col-md-offset-3"><h1>Edit Tag Information</h1></div>
</div>
<br/>
<br/>
{ this.props.ezn != null?
<div>
<ZoneDetailsEdit ezn={this.props.ezn}/>
</div> :
<center><br /><h1><img src={'img/avatars/default.gif'} alt="Loading"/><br />Loading</h1></center>
}
</div>
)
}
}
function mapStateToProps(state) {
return {
ezn: state.zones
}
}
function matchDispatchToProps(dispatch){
return bindActionCreators({fetchedZonesEdit: fetchedZonesEdit}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(ZoneDetailEditComponent);
This is the snippet i provided
what I'm doing right now is i had fetched the data from the api and was shown in the input fields but the issue is data is fetched correctly but when i print outside input fields it works fine but when i enter in input fields as default value it doesn't works
screenshot also provided
when opened the page default values are set of previous page but when it is refreshed it works fine
defaultValue should be used only on Uncontrolled components.
Instead of using defaultValue, assign a default value to the name in your store.
Also, don't use this.setState if you are using redux. You are assigning the new value to this.state.zone which you never read.
Since you are using a controlled component you need not use defaultValue, assigning the props passed down to the value will suffice.
Also using redux, a better practice is to store the UI state in localState and all others in the redux store.
To do that you need to dispatch an action that updates the corresponsing reducer after passing the values to the topmost parent
Also you are not passing any prop as name to the InfoRow component and since defaultValue is rendered only at the time of create, you do not see the update.
You code must look something like
import React, { Component } from 'react';
let _ = require('lodash');
import {bindActionCreators} from "redux";
import {connect} from 'react-redux';
import {fetchedZonesEdit} from '../../actions/';
class InfoRow extends Component {
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
this.props.handleChange(this.props.zone, event.target.value);
}
render() {
return (
<tr>
<td>
{this.props.zone}
</td>
<td>{this.props.zoneValue}
<input type="text"
className="form-control"
value={this.props.zoneValue}
name={this.props.zone}
onChange={this.handleInputChange}
/>
</td>
</tr>
)
}
}
class ZoneDetailsEdit extends Component {
handleChange(zone, value) {
//pass it to the parent and then fire an action from there to update this value in the store
}
render() {
const rows = [];
let a = this.props.ezn;
Object.keys(this.props.ezn).map((keyName, keyIndex) =>{
return rows.push(<InfoRow zone={keyName} zoneValue={a[keyName].toString()} handleChange={()=> this.handleChange}key={keyIndex}/>)
});
return (
<div className="col-md-6">
<div className="">
<table className="table table-clear">
<tbody>
{rows}
</tbody>
</table>
</div>
<div className="row px-1" >
<div className="px-2">
<button className="btn btn-sm btn-info">Save</button>
</div></div>
</div>
)
}
}
Also there is no need for you to bind the lifeCycle function with arrows

Categories