How to make editable cards in React? - javascript

I can't figure out how to make card text editable and reflect that changes in the card state.
So, I have an input field where I type in text and click on add button which makes a call for add function,
addFunction = () => {
this.cardId ++;
this.setState({
t_c: [
...this.state.t_c,
{
cardId: this.cardId,
terms: this.state.terms
}
]
});
}
So terms are the text and its provided with cardId as id.
So to display the data inside state t_c I am mapping each object inside the state and displaying them,
{this.state.t_c.map(terms =>
<section>
<section>
<input defaultValue={terms.terms} />
</section>
<img ..../>
</section>
)}
The problem here if I try to change the added terms the changes are not displayed in the t_c state.

At first, you should approach your problem like it's a Form.
Form components are whether controlled or uncontrolled. For your scenario, if you want to save every change you have done to state, you should implement your input as controlled.
<section>
<input
value={terms.terms}
onChange={(e) => setTerms(/* your state controller code */)}
/>
</section>
Also to see the changes, you are missing key prop to populated sections. Change mapping function to this and you'll see the changes.
{this.state.t_c.map((terms, t_index)=>
<section key={"terms" + t_index}>
<section>
<input defaultValue={terms.terms} />
</section>
<img ..../>
</section>
)}
See more detail here.

Related

Display only value in one line (Comma Separated) from looping through an array of objects

I'm new in react and I've got a problem displaying the only values in JSX. An array of objects is shown below.
const productData=[{"label":"Name","value":"Printer"},{"label":"Name","value":"Mouse"}]
I am trying to display product name with below code but its not displaying anything on page:
{productData.map((prodData) => (
<div>
<h2>
Product:
{' '}
{prodData.value}
</h2>
</div>
))}
im not sure what I am doing wrong, I want to display like:
Printer, Mouse
I think this may be what you want to do :
<div>
<h2>
Product:
{productData.map(prodData => prodData.value).join(', ')}
</h2>
</div>
In your code each iteration is creating a new div and nested dom. Rather you will like to loop over productData inside the h2 and display each of the value in a span
<div>
<h2>
Product:
{productData.map((prodData) => (<span>{prodData.value}</span>))}
</h2>
</div>

How do I toggle on a view more for one component and toggle off the others onClick in React?

I am using a React functional component with ternary to toggle a show more feature. I would additionally like the following logic: if one of the toggles is true (showing more), then the others are false (not showing). Right now, they just stack on top of each other unless you specifically click the button for each to "off" again.
This is them in initial state:
const [showFirst, setShowFirst] = useState(false)
const [showSecond, setShowSecond] = useState(false)
Here's the login in the render to show one of the 'show mores':
<button
className="btn-show"
onClick={() => setShowFirst(!showFirst)}>
<p className="proj-name">86 List</p>
</button>
Here's how it displays in its proper place:
{showFirst ? (
<div className="hide-me">
<p className="proj-lang">Built with: React.js, Ruby on Rails, Postgresql, CSS</p>
<p className="proj-desc">86List is a community for service-industry professionals to talk about the clients that they serve. Built with Ruby on Rails and React.js, 86List requires login authentication and registration in order to view and interact with co-workers' posts. In future iterations, I want to allow users to create their own accounts and request to be a part of a community with a community leader's approval.</p>
<img className="proj-img" src="/assets/86list.png" alt="" />
<div className="proj-link-container">
<a className="proj-link" href="https://github.com/aawferris/86list" target="_blank" rel="noreferrer" alt="this repo's github">REPO</a>
<a className="proj-link" href="https://86list.netlify.app/" target="_blank" rel="noreferrer" alt="live site for this link">SITE</a>
</div>
</div>
) : (
<div></div>
)}
I have tried making a handleClick that would toggle each back to false and the current one true, but it didn't work. Here's an example:
const handleFirst = () => {
setShowFirst(!showFirst)
setShowSecond(showSecond)
}
Thanks in advance!
Andy
The Idea would be to have an array of objects where you have an attribute like
showMore: true/false and also some sort of an id like id: 1. Each time one of the buttons is clicked, you iterate over the array (which should be a state in the parent component), you set the showMore attribute of that clicked button to true and set all the other ones to false. Then you just set that array as your new State.

onClick Removing data from components

So basically I have two components. In Component 1 there is an img. In component 2 there is a button. I need when i press the button img disappears and also button disappears.
const Icons=()=> {
return (
<div className="_icons">
<div className="icons__Top">
<img src="./icons/-48.png" alt="Twitter"/>
<figcaption>Whatever</figcaption>
</div>
</div>
const Button= () => {
return (
<div className="button">
<Button variant="outlined" className="button__rightpage" >REMOVE</Button>
<caption className="text" ></caption>
</div>
)
}
There can be more than one solution,
1)I think you need to have one parent component for both components and make one state in parent component and then pass it in icon component and give it to img and default make it to false and then from button component change that state to true.
2)without parent component -> You can also use context api to change value directly from button component and hide img.

React.js - Creating Simple Accordion example

I'm relevantly new to React and I am having trouble on how to tackle this logic:
Essentially I am creating a table using flexbox, and I also want it so that when you click on one of the rows, it expands and reveals another row (for example, it will give a small description what it is about).
So far what I have is just the table.
class Application extends React.Component {
render() {
const renderDataRows = (
[
<div key={0} className='row'>
<div className='cell description'> Mortage Bill
</div>
<div className='cell amount'>$0,000,000</div>
<div className='cell amount'>$2.50</div>
<div className='cell amount'v>000%</div>
</div>,
<div key={1} className='row'>
<div className='cell description'> Electric Bill
</div>
<div className='cell amount'>$0,000,000</div>
<div className='cell amount'>$2.50</div>
<div className='cell amount'v>000%</div>
</div>,
]
)
const containerTable = (
<div className='table-container'>
{renderDataRows}
</div>
)
return (
<div>
{containerTable}
</div>
)
}
}
More specifically, what would be the best way to structure the hidden rows? Create as a child of the cells, or siblings?
I am assuming I will need state to keep in track what is current open, etc?
I've attached Codepen link to mess around
This can be done in the following way:
Let all the cells be in a single parent div and let the cell description be another sibling div (although using would be better). Put a class on the sibling div such as hidden. Not add a click handler on the cells div. Whenever this div is clicked, update the state with that div's id/key. Now use this to set the hidden class to the other divs. Compare this.state.key with the current id/key and show or hide accordingly. I am not giving the specific code.
Note: Instead of storing the divs in the renderDataRows, just put the data in it and map over it to create all the divs. That way you can easily manipulate the hidden class and any other variation in a single place without having to update it separately for each row of data.

Learning React.js

I am trying to learn React. I already have a good grasp of javascript. I am trying to learn by creating a small app that is basically a task manager. In my case it's for grocery related items. I have a fiddle created here. Could you please take a look at how I composed the react code and let me know if this is the best approach to building with components/classes? You can see that I have nested components. I am not sure if there is a better way of doing this.
Finally, I wan't a new "add-item-row" created every time a user clicks on the big blue Add button. Right now one is showing be default but I don't want any showing by default. I want one created (add-item-row, div) only when a user clicks on the Add button.
Here is the fiddle.
https://jsfiddle.net/j0mpsbh9/4/
<div id="app" class="container">
<script type="text/babel">
var AddItemWrapper = React.createClass({
render: function() {
return (
<div>
<div className="row">
<AppTitle />
<AddItemForm />
</div>
<AddItemRow />
</div>
);
}
});
var AppTitle = React.createClass({
render: function() {
return (
<div>
<h1>Grocery List</h1>
</div>
);
}
});
var AddItemForm =React.createClass({
render: function() {
return (
<div>
<div className="col-sm-6 col-lg-6">
<div className="form-group">
<label htmlFor="enter-grocery-item" className="sr-only">Enter Grocery Item</label>
<input type="text" className="form-control" id="enter-grocery-item" placeholder="Enter Grocery Item" />
</div>
</div>
<div className="col-sm-6 col-lg-6">
<button type="button" id="add" className="btn btn-block btn-info">Add <span className="glyphicons circle_plus"></span></button>
</div>
</div>
);
}
});
var AddItemRow =React.createClass({
render: function() {
return (
<div className="add-item-row">
<div className="row">
<div className="col-sm-12 grocery-items">
<div className="col-sm-6">
<div className="form-group">
<label htmlFor="grocery-item" className="sr-only">Grocery Item</label>
<input type="text" className="form-control" id="grocery-item" placeholder="" />
</div>
</div>
<div className="col-sm-6 center">
<button type="button" className="btn btn-blockx btn-warning"><span className="glyphicons pencil"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-danger"><span className="glyphicons remove"></span></button>
<button type="button" className="btn btn-blockx btn-lgx btn-success"><span className="glyphicons thumbs_up"></span></button>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<AddItemWrapper />,
document.getElementById('app')
);
</script>
</div>
You have a good start here! Your component hierarchy is organized in a sensible way. However you are missing any kind of interactivity or internal state.
The main way you make React components interactive is by using state plus event callbacks which modify said state. "State" is pretty self explanatory - it describes values inherent to how the components looks and behaves, but which change over time. Every time a React component's state is altered (with this.setState()) that component will re-render (literally by re-running the render() function) to reflect the changes.
First let's edit your AddItemWrapper class to keep track of some internal state when it is first mounted. We know that you want to have multiple rows of data, so let's give it an empty array to store future information about rows:
getInitialState(){
return {rows: []};
},
Now instead of rendering a single AddItemRow directly, we'll render a dynamic set of rows that is based on the current component state. Array.map() is perfect for this and a common use case in React:
{this.state.rows.map(function(ea, i){
return <AddItemRow initialItemName={ea} key={ea + "-" + i} />
})}
Basically what that does is take every entry in the array AddItemWrapper.state.rows array and renders it as an AddItemRow component. We give it two properties, initialItemName and key. "initialItemName" will just tell the child component what its name was when it was first added, and "key" is a unique string that allows React to differentiate components from their siblings.
Now we've set up AddItemWrapper to properly render rows based on its internal state. Next we have to modify AddItemForm so that it will react to user input and trigger new rows being added.
In AddItemForm, first we need to add a "ref" to the input text box. This is so that React can identify and read data from this HTML element after it is rendered:
<input ref={function(el){this.inputElement = el;}.bind(this)} ... />
Then give the button element a callback that will trigger when it's clicked:
<button onClick={this.handleClick} ... />
Finally write the callback handler itself:
handleClick(){
this.props.onAdd(this.inputElement.value);
this.inputElement.value = "";
}
Notice how this callback is calling this.props.onAdd()? That means we need to pass in a callback function from the parent (AddItemWrapper) to this component to use. This is how we communicate between parents and children in React: pass a function from a parent to a child which will be triggered from within the child, but will effect the parent.
In AddItemWrapper we make sure AddItemForm has access to the callback function:
AddItemForm onAdd={this.onAdd} />
And then we write the callback function itself:
onAdd(newItem){
var newRows = this.state.rows.slice();
newRows.push(newItem);
this.setState({rows: newRows});
}
Notice how we're copying the old array held in state (using Array.slice()), push a new item into the new array, and then update state with the new array? Never mutate state directly; ALWAYS copy it, modify the copy, and then update state with the new copy.
Almost done. We've created a way for AddItemWrapper to render its rows, and a way for AddItemForm to create new rows. Now we edit AddItemRow to render in a way that maintains its own internal state too.
First make sure it initializes its own state when it's mounted. We'll have it keep track of a string value, which initially is the same as what the user entered into the text box when they pressed "Add", but because it's kept in AddItemRow.state it can be modified later by the user:
getInitialState() {
return {itemName: this.props.initialItemName}
}
Now that the row name is kept in the component state, we can render it in the HTML like this:
<input value={this.state.itemName} ... />
Here's what it looks like when you put it all together!
There are obviously more features that you would want to add, such as letting the user edit, move, or delete a row entry. I'll leave those exercises up to you. I highly recommend you read through all of the official documentation as well as do a few tutorials to get your head in the game. It's obvious that you have a bit of experience under your belt given what you had so far, but getting the hang of how state, render(), and callbacks work takes some practice. Good luck!

Categories

Resources