I have a table that has been created dynamically, and am attempting to put full CRUD capabilities onto it. For business reasons I am unable to use external libraries for this, and so have resulted in using basic HTML with react. I am currently trying to detect changes within the data. My problem is in regards to the onInput event with the div inside the tag. When the components are first initialized with the data, the onInput event fires for each one rather than waiting for an actual user input. To a degree I understand why this is happening, but I am in need of a workaround for it or an alternative. I have created a small demo below to show a mock of the current code:
Parent class:
import React, {Component} from 'react';
class FormContainer extends Component{
constructor(props){
super(props)
this.state={
rowData : myData
}
this.onInput = this.onInput.bind(this)
}
onInput = (rowKey) => {
console.log(rowKey)
}
render() {
return(
<Grid
data={this.state.rowData}
onInput={this.onInput}
/>
)
}
}
Grid class:
import React, {Component} from 'react';
class Grid extends Component{
constructor(props){
super(props)
}
render(){
let columns = [];
let rows = [];
if(this.props.data != null){
columns = this.props.data.slice(0, 1).map((row, i) => {
return(
Object.keys(row).map((column, key) => {
return(
<th key={key}>{column}</th>
)
})
)
})
rows = this.props.data.map((row, rowKey) => {
return(
<tr key={rowKey}>
{Object.keys(row).map((data, cellKey) => {
return(
<td key={cellKey} suppressContentEditableWarning="true" contentEditable="true" onChange={this.props.onInput(rowKey)}>{row[data]}</td>
)
})}
</tr>
)
})
}
return(
<table>
<thead><tr>{columns}</tr></thead>
<tbody>{rows}</tbody>
</table>
)
}
}
export default Grid;
The issue is that when your component is rendered any time, you are calling your onInputmethod:
<td
key={cellKey}
suppressContentEditableWarning="true"
contentEditable="true"
--> onChange={this.props.onInput(rowKey)}>{row[data]}</td>
Instead of calling it you have to pass the a function, in this case you can pass an anonymous function or an arrow function:
<td
key={cellKey}
suppressContentEditableWarning="true"
contentEditable="true"
--> onChange={ () => { this.props.onInput(rowKey); } }>{row[data]}</td>
Related
I want to create a function which iterate over all element with same class and remove a specific class.
It could be done easily using JavaScript.
const boxes = document.querySelectorAll(".box1");
function remove_all_active_list() {
boxes.forEach((element) => element.classList.remove('active'));
}
But how can I do this similar thing is ReactJs. The problem which I am facing is that I can't use document.querySelectorAll(".box1") in React but, I can use React.createRef() but it is not giving me all elements, it's only giving me the last element.
This is my React Code
App.js
import React, { Component } from 'react';
import List from './List';
export class App extends Component {
componentDidMount() {
window.addEventListener('keydown', this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == '38' || e.keyCode == '40') this.remove_all_active_list();
};
remove_all_active_list = () => {
// boxes.forEach((element) => element.classList.remove('active'));
};
divElement = (el) => {
console.log(el);
el.forEach((element) => element.classList.add('active'))
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import data from './content/data';
export class List extends Component {
divRef = React.createRef();
componentDidMount() {
this.props.divElement(this.divRef)
}
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div className="box1" id={i} ref={this.divRef} key={src}>
<img src={src} title={title} align="center" alt={title} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Please tell me how can I over come this problem.
The short answer
You wouldn't.
Instead you would conditionally add and remove the class to the element, the component, or to the collection.map() inside your React component.
Example
Here's an example that illustrates both:
import styles from './Example.module.css';
const Example = () => {
const myCondition = true;
const myCollection = [1, 2, 3];
return (
<div>
<div className={myCondition ? 'someGlobalClassName' : undefined}>Single element</div>
{myCollection.map((member) => (
<div key={member} className={myCondition ? styles.variant1 : styles.variant2}>
{member}
</div>
))}
</div>
);
};
export default Example;
So in your case:
You could pass active prop to the <ListItem /> component and use props.active as the condition.
Alternatively you could send activeIndex to <List /> component and use index === activeIndex as the condition in your map.
Explanation
Instead of adding or removing classes to a HTMLElement react takes care of rendering and updating the whole element and all its properties (including class - which in react you would write as className).
Without going into shadow dom and why react may be preferable, I'll just try to explain the shift in mindset:
Components do not only describe html elements, but may also contain logic and behaviour. Every time any property changes, at the very least the render method is called again, and the element is replaced by the new element (i.e. before without any class but now with a class).
Now it is much easier to change classes around. All you need to do is change a property or modify the result of a condition (if statement).
So instead of selecting some elements in the dom and applying some logic them, you would not select any element at all; the logic is written right inside the react component, close to the part that does the actual rendering.
Further reading
https://reactjs.org/docs/state-and-lifecycle.html
Please don't hessitate to add a comment if something should be rephrased or added.
pass the ref to the parent div in List component.
...
componentDidMount() {
this.props.divElement(this.divRef.current)
}
...
<div ref={this.divRef} className="container1">{listItem}</div>
then in App
divElement = (el) => {
console.log(el);
el.childNodes.forEach((element) => element.classList.add('active'))
}
hope this will work. here is a simple example
https://codesandbox.io/s/staging-microservice-0574t?file=/src/App.js
App.js
import React, { Component } from "react";
import List from "./List";
import "./styles.css";
export class App extends Component {
state = { element: [] };
ref = React.createRef();
componentDidMount() {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.add("active"));
console.log(divRef);
window.addEventListener("keydown", this.keypressed);
}
keypressed = (e) => {
if (e.keyCode == "38" || e.keyCode == "40") this.remove_all_active_list();
};
remove_all_active_list = () => {
const {
current: { divRef = [] }
} = this.ref;
divRef.forEach((ele) => ele?.classList?.remove("active"));
// boxes.forEach((element) => element.classList.remove('active'));
console.log(divRef);
};
render() {
return (
<div className="container0">
<List divElement={this.divElement} ref={this.ref} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from "react";
import data from "./data";
export class List extends Component {
// divRef = React.createRef();
divRef = [];
render() {
let listItem = data.map(({ title, src }, i) => {
return (
<div
className="box1"
key={i}
id={i}
ref={(element) => (this.divRef[i] = element)}
>
<img src={src} title={title} align="center" alt={title} width={100} />
<span>{title}</span>
</div>
);
});
return <div className="container1">{listItem}</div>;
}
}
export default List;
Create ref for List component and access their child elements. When key pressed(up/down arrow) the elements which has classname as 'active' will get removed. reference
I have two components, one that contains a checkbox and one that's a button. The intent is that this former component let's call it Row, if the checkbox is changed, the edit button would enable itself, and if the checkboxes are not ticked anymore the Edit button disables itself. I was planning on adding onclick event listeners but then I recently read about states and figured this is probably a better idea.
This is my Button:
class EditButton extends React.Component {
constructor() {
super();
this.state = {
clickable: false
}
}
render() {
const {clickable} = this.state
if (clickable) {
return (
<Button className="button-amber">Edit</Button>
)
} else {
return (
<Button disabled>Edit</Button>
)
}
}
}
and this is my Row component
class MyRow extends React.Component {
constructor(props) {
super(props);
this.payload = this.props.payload;
}
render() {
const toRoute = "/foo/" + this.props.payload["id"]
const tags = []
for (const [index, value] of this.props.payload["tags"].entries()) {
tags.push(<Badge>{value}</Badge>)
}
return (
<tr className="white-text">
<td>
<Form.Group>
<Form.Check type="checkbox"/>
</Form.Group>
</td>
<td>
STUFF
</td>
<td>
{this.payload["label"]}
</td>
<td>
{tags}
</td>
<td>
</td>
</tr>
);
}
}
My main app render Might look like so
render(){
<div>
<EditButton/>
<Table>
<MyRow payload=.../>
<MyRow payload=.../>
</Table>
</div>
}
And my intent is if the checkbox is clicked, check the state of all checkboxes to ensure that something is checked, if any checkbox is checked, then change the EditButton clickable state to true. What's the right way to do this? Normally I would use event listeners and individual selectors, but given that I'm using react feels like there should be a more straightforward way to modify the state of that component
There are basically two ways to share state between multiple components:
Shift state into a parent component.
Store the state externally using React Context or a state framework like Redux.
For your specific use case, I would suggest going for the first option. A good way of deciding when to use each option is to decide if the state is local to the sibling components or if it should be globally visible. Meaning that not every bit of your app state needs to be centrally managed.
Using your example, I created this snippet to show how it might work:
class Button extends React.Component {
render() {
const {onClick, disabled} = this.props
return (
<button onClick={onClick} disabled={disabled}>
Button
</button>
)
}
}
class Row extends React.Component {
render() {
const {checked, onChange} = this.props
return (
<input type="checkbox" checked={checked} onChange={onChange} />
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
checked: false
}
this.handleCheckboxChange = this.handleCheckboxChange.bind(this)
this.handleButtonClick = this.handleButtonClick.bind(this)
}
handleCheckboxChange(e) {
this.setState({
checked: e.target.checked
})
}
handleButtonClick() {
console.log("Clicked")
}
render() {
const {checked} = this.state
return (
<div>
<Row checked={checked} onChange={this.handleCheckboxChange} />
<Button disabled={!checked} onClick={this.handleButtonClick} />
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"/>
The App component handles the state of both the Row and Button components. You can handle how the child components will modify the parent state by providing callbacks that they will call upon some event, like toggling a checkbox.
React's built in state management is very limited, it's the reason a lot of users like to use Redux to handle more complex state were lots of controls interact. Personally I use my own state management using Proxys.
But you can pass setState's between components by passing them via props, below I've created a var called state that keeps track of the useStates for each component. This then allows independent setState calls between components.
I've also used React hooks here instead of class based, but the concept is the same for both..
function Button({state}) {
const butState = React.useState(false);
const [butEnabled] = butState;
state.butState = butState;
return <button
disabled={!butEnabled}
>Button</button>;
}
function Row({state, row}) {
const rowState = React.useState(false);
const [checked, setChecked] = rowState;
state.rows[row] = rowState;
function toggleChecked() {
state.rows[row][0] = !checked;
state.butState[1](state.rows.some(b => b[0]));
setChecked(state.rows[row][0]);
}
return <div>
<input value={checked} type="checkbox" onChange={toggleChecked}/>
</div>
}
function Page() {
const state = {
rows: []
}
return <div>
<Button state={state}/>
<Row state={state} row={1}/>
<Row state={state} row={2}/>
</div>
}
ReactDOM.render(
<Page/>,
document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="mount"/>
I tried to solve this js react problem and get stuck on questions 2-4.
Question 2: I don't know how to check the local state for each row in order to check for the duplicate rank select
Question 3: Should I need props passed to the component to check for unique?
Question 4: How do I check all rows have a select ranked and unique?
Here are the questions:
Adding a class of "done" to a row will highlight it green. Provide this
visual feedback on rows which have a selected rank.
Adding a class of "error" to a row will highlight it red. Provide this
visual feedback on rows which have duplicate ranks selected.
There is a place to display an error message near the submit button. Show
this error message: Ranks must be unique whenever the user has selected the
same rank on multiple rows.
The submit button is disabled by default. Enable it when all rows have a
rank selected and all selected ranks are unique.
The orginal App.js
import React, { Component } from 'react';
import './App.css';
import MainPage from './components/MainPage';
class App extends Component {
render() {
return (
<MainPage />
);
}
}
export default App;
MainPage.js
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" />
</div>
);
}
}
export default MainPage;
FormRow.jsx
import React from 'react';
import _ from 'lodash';
class FormRow extends React.Component {
render() {
const cells = _.range(1, 6).map((i) => {
return (
<td key={`${this.props.animalName}-${i}`}>
<input
type="radio"
name={this.props.animalName}
value={i}
/>
</td>
);
});
return (
<tr>
<th>{this.props.animalName}</th>
{cells}
</tr>
)
}
}
export default FormRow;
Animal.js
class Animal {
constructor(name, rank) {
this.name = name;
this.rank = rank;
}
}
export default Animal;
My code is at GitHub (git#github.com:HuydDo/js_react_problem-.git). Thanks for your suggestion!
FormRow.jsx
import React from 'react';
import _ from 'lodash';
class FormRow extends React.Component {
constructor(){
super();
this.state = {
rowColor : false,
name: "",
rank: 0
// panda: 0,
// cat: 0,
// capybara: 0,
// iguana: 0,
// muskrat: 0
}
}
handleChange = (e) => {
if (this.state.rank === e.target.value){
console.log("can't select same rank.")
}
console.log(e.target.name)
console.log(e.target.value)
this.setState({
// [e.target.name]: e.target.value,
name: e.target.name,
rank: e.target.value,
rowColor: true
}, console.log(this.state))
}
handleChange2 = (e) => {
let newName = e.target.name
let newRank = e.target.value
let cRank = this.state.rank
let cName = this.state.name
console.log(this.state)
console.log(`${newRank} ${newName}`)
if(cName !== newName) {
if(cRank !== newRank) {
this.setState({
name : newName,
rank: newRank,
rowColor: true
},()=> console.log(this.state))
}
else {
console.log("can't select same rank")
}
}
// this.setState(previousState => {
// let cRank = previousState.rank
// let cName = previousState.name
// console.log(previousState)
// return {
// rank: newRank,
// name: newName,
// rowColor: true
// }
// },console.log(this.state.rank))
}
render() {
const cells = _.range(1, 6).map((i) => {
return (
<td key={`${this.props.animalName}-${i}`} onChange={this.handleChange2}>
<input
type="radio"
name={this.props.animalName}
value={i}
/>
</td>
);
});
return (
<tr className = {(this.state.rowColor) ? 'done':null} >
{/* <tr> */}
<th>{this.props.animalName}</th>
{cells}
</tr>
)
}
}
export default FormRow;
MainPage.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
getValue = ({name,rank}) =>{
console.log(`Name: ${name} rank: ${rank}`)
}
// handleSubmit = event => {
// event.preventDefault()
// this.props.getValue(this.state)
// }
checkForUnique = () => {
// Show this error message: `Ranks must be unique` whenever the user has selected the
// same rank on multiple rows.
this.setState({
error : "Ranks must be unique"
})
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
handleChange={this.handleChange}
getValue={this.getValue}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} /> {/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
enter image description here
I tried to add handleChange and handleAnimalSelect methods, but I get an error. The new name and rank are not added to the arrays.
MainPage.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
getValue={this.getValue}
handleAnimalSelect={this.handleAnimalSelect}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} />
{/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
FormRow.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
getValue={this.getValue}
handleAnimalSelect={this.handleAnimalSelect}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} />
{/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
You're pretty much making a form with a few rows of multiple choice answers.
One way of simplifying everything is to have all the logic in the top component, in your case I think MainPage would be where it would be. Pass down a function as a prop to all the descendants that allows them to update the form data upstream.
In Q2, how do intend to check the state for each row? Perhaps you can use arrays or objects to keep track of the status of each question. The arrays/objects are stored in state, and you just check them to see what the status is.
I'm actually not clear what your app looks like - what does a row look like? (You might want to post a screenshot) And I don't see any way for rank to be selected - I don't even see what the ranks are for, or how they are used in the form. So perhaps your form design needs to be tweaked. You should begin the form design with a clear picture in YOUR mind about how the app will work. Maybe start by drawing the screens on paper and drawing little boxes that will represent the objects/array variables and go through the process of a user using your app. What happens to the various boxes when they click radio buttons and so on. How will you know if the same rank is selected twice - where are the selected ranks stored? What animals are clicked/selected? Where are those stored? Draw it all on paper first.
Array or objects: If you want to keep it simple, you can do the whole project just using arrays. You can have one array that stores all the animals. You can have a different array that stores which animals are selected right NOW (use .includes() to test if an animal is in that array). You can have another array that stores the rows that have a rank selected. When the number of elements in that row === the number of rows (is that the same as the number of animals? If yes, then you can use the length of the animals array for that test)
How do you know if the rows with a rank selected are unique? One way is to DISALLOW selected a rank that has already been selected. Again, use .includes() (e.g. arrSelectedRanks.includes(num)) to check if a rank has already been selected.
SO what do one of these checks look like?
const handleAnimalSelect = (animal) => {
const err = this.state.selectedAnimals.includes(animal);
if (err === true){
this.setState(error: animal);
}
}
return (
<input
type="radio"
name={this.props.animalName}
value={i}
onClick={this.handleAnimalSelect}
/>
{ error !== undefined && (
<div class={style.errorMessage}>{`Animal ${} was chosen twice`}</div>
)}
);
};
Remember: State is what you use for remembering the value of variables in a given component. Every component has its own state. But Props are for data/functions/elements that are passed into the component. You don't update the values of props in the component (prop values are stored in another component. If you need to update them, you use functions to pass data back to the parent component where that variable is in state, and update the value there).
Here is an example that uses .includes() to check for the presence/absence of something:
https://stackoverflow.com/a/64486351/1447509
Component child in React.js
I am creating an app which can be seen as a crypto-portfolio list. Every coin item has some details like holdings and price. These are saved in a dictionary saved in localstorage and loaded in the portfolio container components' state.
The hierarchy can be seen as Container > PortfolioList > Portfolio Item.
My question is as follows: onClick of the Portfolio Item, I want to populate an entirely different component with an API call of that Portfolio Coin ( to show an historical graph ), called PortfolioDetails.
Question
How is this component - global interaction best handled in React.js? Is it best practice to define a state in the PortfolioContainer and change it upon clicking on the PortfolioItem child (passing it through 2 parents), or is there a way to re-render the PortfolioDetails component onClick, like I tried here?
Thank you!
export default class PortfolioContainer extends React.Component{
constructor(props){
super(props);
this.state = (
{portfolio:
{ICX:{transactions:[{purchase: '$49.99', amount: 50}], price:{raw:{BTC:{PRICE:3.20}}, display:{BTC:{PRICE:3.20}}}},
currency: 'BTC',
total: 0
}
)
}
render(){
return(
<Container>
<Row>
<Col xs="12">
<PortfolioList currency={this.state.currency} portfolio={this.state.portfolio} />
<PortfolioDetails show='portfolio'/>
</Col>
</Row>
</Container>
);
}
}
export class PortfolioList extends React.Component{
constructor(props){
super(props);
}
render(){
const rows = [];
if(Object.keys(this.props.portfolio).length > 0){
Object.keys(this.props.portfolio).map((coin, details) =>{
rows.push(
<CoinRow
coin={coin}
details={this.props.portfolio[coin]}
currency={this.props.currency}
key={coin}
/>
)
});
}
return(
<table>
<thead>
<tr>
<th>Coin</th>
<th>Holdings</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
);
}
}
export class CoinRow extends React.Component{
constructor(props){
super(props)
this.handleClick = this.handleClick.bind(this);
}
handleClick(e){
e.preventDefault();
// Get coin ID from clicked event
var coinId = e.currentTarget.attributes['data-id'].value;
// Populate portfolio details with coin details
<PortfolioDetails show={coinId}/>
}
render(){
const coin = this.props.coin;
const details = this.props.details;
var holdings = null;
return (
<tr data-id={coin} onClick={this.handleClick}>
<td>{coin}</td>
<td>{holdings}</td>
<td>{details.price.display[this.props.currency].PRICE}</td>
</tr>
);
}
}
export class PortfolioDetails extends React.Component{
constructor(props){
super(props);
}
render(){
var showing = this.props.show;
return(
<div>
// Showing details for the following coin
<p>{showing}</p>
</div>
);
}
}
The best way to do what you want is using Redux.
This way you would have a flow like this:
Can you see that the UI is defined by the State and there is a unique Store that contains the application State? That's why Redux works nicely in your use case.
You can keep a State in your Store like:
cryptoPorfolio: { // this is the name of the reducer, it will be part of the state tree, read below
coin: ''
}
Now you can update this State firing an action in the 'UI', that will be sent to the Reducer that finally makes the update. Something like this:
Action
{
type: 'UPDATE_COIN',
coinName: 'ETH'
}
Action Creator (just functions that can be triggered by the UI)
function updateCoin(coin) {
return {
type: 'UPDATE_COIN',
coinName: 'Ethereum'
}
}
You can use bindActionCreators from react-redux to pass your action creator to your component.
Then, in your Component, you would call this action creator like a normal function passed as a prop in order to send an action to the reducer.
Finally, in the reducer, you can update the Store. For example:
function cryptoPorfolio(state = initialState, action) {
switch (action.type) {
case UPDATE_COIN:
return Object.assign({}, state, {
coin: action.coinName
})
default:
return state
}
}
For more detailed info, read Redux usage with React.
Yes the best practice would be to have the PortfolioContainer manage the state of what children are hidden/shown. That would be the easiest way to get the whole component tree to update. You can have a handlePortfolioItemClick method on there, which can update the state to show the different data inside PortfolioDetails.
I have a dropdown populated from a Web Service, what I want is to display some text according to the selection made. For example the first option in the Dropdown is Buy n and Save m so in a p tag I want to display Buy 2 and Save $1.5 I know this is work for a switch and the position of the array is going to be my "CASE" in order to know what to display or not but I'm new to react and also in programming so I need help..
import React from 'react';
import DropDownMenu from 'material-ui/DropDownMenu';
import MenuItem from 'material-ui/MenuItem';
import cr from '../styles/general.css';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
OfferTypeData: [],
OfferTypeState: '',
};
this.handleChange = this.handleChange.bind(this);
this.renderOfferTypeOptions = this.renderOfferTypeOptions.bind(this);
}
componentDidMount() {
const offerTypeWS = 'http://localhost:8080/services/OfferType/getAll';
fetch(offerTypeWS)
.then(Response => Response.json())
.then(findResponse => {
console.log(findResponse);
this.setState({
OfferTypeData: findResponse
});
});
}
handleChange(event, index, value) {this.setState({value});}
handleChangeDiscountType(event, index, value) {
this.setState({ OfferTypeState: (value) });
}
renderOfferTypeOptions() {
return this.state.OfferTypeData.map((dt, i) => {
return (
<MenuItem
key={i}
value={dt.offerTypeDesc}
primaryText={dt.offerTypeDesc} />
);
});
}
render() {
return (
<div className={cr.container}>
<div className={cr.rows}>
<div>
<DropDownMenu
value={this.state.OfferTypeState}
onChange={this.handleChangeDiscountType}>
<MenuItem value={''} primaryText={'Select Offer Type'} />
{this.renderOfferTypeOptions()}
</DropDownMenu>
<br/>
<p>{DISPLAY SOME TEXT HERE}</p>
</div>
</div>
</div>
);
}
}
Thanks in advance!
Regards.
Create a component which passes a callback to the dropdown, this callback will update the state of the container which will in turn set the props of the display. This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
A rough layout would be something like this.
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.changeText = this.changeText.bind(this);
}
changeText(text) {
this.setState({text: text});
}
render() {
return (
<DropDown callback={this.changeText} />
<Display text={this.state.text} />
)
}
}
Display component...
const Display = (props) => (
<p>{this.props.text}</p>
)