react loop update state - javascript

I'm new to react and what I'm doing is loop to get to show the each element form the props and I want form the picture component update that props, I try to find a way to do it but I didn't know how to do it.
Code for the loop is this:
const pictureItems = this.props.imgFiles.map((img, index) => {
return <picture key={index} imgFile={img} pictureDataUpdate={this.onUpdatPicture} />;
});
The question is how can I update the props that are been pass to the picture component? (I'm already passing the information from picture to the component that is looping). I have so far this.
onUpdatPicture(data) {
console.log(data);
// this.setState(data);
}

The simplest method for manipulating props sent to a child component would be to store the data in the parent component's state. Doing so would allow you to manipulate the data and send the updated version to your child component.
Assuming our parent component is sent an array of image urls as the images prop, we'll need two main pieces in our code: our update function for our child to call and to map over our images and create our children.
class Gallery extends React.Component {
constructor(props) {
super(props)
//Setting our props to the state of the parent allows us to manipulate the data before sending it back to our child.
this.state = {
images: this.props.images || []
}
}
update = (key, value) => {
// Our update function is sent the {key} of our image to update, and the new {value} we want this key to hold.
// After we are passed our data, we can simply map over our array and return the new array to our state.
this.setState({
images: this.state.images.map( (img, i) => i === key ? value : img)
})
};
render() {
return (
<div className="gallery"> // Since we are going to have multiple children, we need to have a wrapper div so we don't get errors.
{
// We map over our data and send our child the needed props.
// We send our child the {src} of our image, our {update} function, the id our child will use to update our parent, and a key for React to keep track of our child components
images.map( (img, i) => <Picture src={img} update={this.update} id={i} key={'picture_' + i} />)
}
</div>
)
}
}
After we have our update function setup and our parent is mapping over our images to create the child component, all that's left to do is setup our child component to handle our data.
class Picture extends React.Component {
render() {
return (
// Notice our onClick is an arrow function that calls our update method. This is so we only call our update function once the onClick is fired, not when the component is being rendered.
<div className="picture" onClick={() => this.props.update(this.props.id, 'https://static.pexels.com/photos/189463/pexels-photo-189463.png')}>
<img src={this.props.src} />
</div>
)
}
}
Given the above code, once we render our gallery component, anytime an image is clicked, the child's image is replaced with a new image.
Here is a link to a working example on CodePen.

Related

React cannot update during an existing state transition (such as within render ) functional component

I am dealing with two components and one store.Components are Child and Parent,Store is store. Parent when rendering, make the child component up and parent listens to store.Now I want to update the state of variable in Store, by exposing a method from Store. The parent pass this method as a call back to child. Child has a dialog box, and if user closes/dismiss that dialog box, the clearStoreInfo() should be called, which is passed by parent to child. But when i do so, I get error
React cannot update during an existing state transition (such as within render ) functional component
Store looks as :
class Stores {
........
........
........
#action.bound
clearStoreInfo = () => {
console.log(" clear info");
}
}
Parent Component:
import Store ........
class InfoContainer extends React.Component {
return (
<div id={"info"} className={"container"}>
<ButtonPanel store={Store} />
</div>
}
const ButtonPanel = (props) => {
return (
.....
.....
<div>
<ChildComponent clearStoreInfo={props.store.clearStoreInfo}/>
</div>
......
}
class ChildComponent extends React.Component {
render(){
const {clearStoreInfo} = this.props;
return (
////if -else ladder
clearStoreInfo(); // warning line.
<Dialog.......
onClose={clearStoreInfo}
/>
)
}
//Because of warning, the dialog box dont come. If i remove clearStoreInfo(), then dialog box works fine.
Is there any easy workaround given these components and store?
The wiring works fine, because I can see console log,based on if-else ladder, but its just the warning is messing things up internally.onClose is able to call the clearStoreInfo perfectally, but its when I manually call it, I am getting a warning.

React Native - Child Component won't finish rendering after Child Event called that updates Parent's state

In my project, I'm looping through cards in my Child Component, and updating the Parent's state with ones where I activate an event of 'swiping right' on the card to favorite. The parent's state keeps track of all favorites by adding the favorited card to the array.
I'm passing down a function (updatestate)from the Parent Component, App, to the Child, Main, that allows the Child to call .setState() and append to the array in the Parent state.
But, when I activate the eventhandler onSwipedRight inside the Child Component, the parent's state gets updated as planned with the new Card, but nothing below the <Card> in Main gets rendered automatically for the next card, as it should. If I tap the screen, then the next card/picture renders only then.
Any ideas? Am I missing some binding or need to do some componentDidMount or anything so the code in child component, Main, renders even after I activate the event handler that updates the parent state?
Basically, is there a tool in React to make sure something renders or at least waits for it to render? (post event handling which sets the parents state in my case)
collection = imagedata;
//collection is the data (local JSON) i'm looping thru via .map in Main Component
const RootStack = StackNavigator(
{
Main: {
screen: Main}
}
);
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
favoritesList: []
};
}
updateArr=(itemname, url)=>{this.setState({ favoritesList: [...this.state.favoritesList, {"item_name":itemname, "url":url}]})};
render() {
return <RootStack screenProps={{appstate: this.state,
updatestate: this.updateArr}}
/>;
}
}
class Main extends React.Component {
render() {
var updatestate = this.props.screenProps.updatestate;
const contents = collection.map((item, index) => {
return (
<Card key={index}
onSwipedRight={() => {updatestate(item.item_name,item.url)}}
>
<View> //THIS and anything after <Card> doesn't render for the next card automatically if I do 'onSwipedRight'
<Image
source={{uri: item.url}} />
</View>
</Card>
)
},this);
return (
<View>
<CardStack>
{contents}
</CardStack>
</View>
);
}
}
(abbreviated) Project structure:
App
|__ Rootstack
|
|__Main
UPDATE (more info):
Just to test the event handler, I added in a function that doesn't set the state of the parent, and had <Card> call that on the event handler -- it works perfectly and the child component <Card> renders perfectly. It seems that it's the updatestate function passed down from the parent to the child that acts to call .setState() upstream that for some reason is causing the Child to not render/not finish rendering after the event handler.
class Main extends React.Component {
render() {
var updatestate = this.props.screenProps.updatestate;
var newfunc = (a, b) => {console.log('a:', a, 'b:', b)};
const contents = collection.map((item, index) => {
return (
<Card key={index}
newfunc(item.item_name,item.item_name);}}
// onSwipedRight={() => {updatestate(item.item_name,item.url); }}
>
If you need to set the state of a component based on the previous state, you should use
this.setState((prevState, props) => {})
Which should, in your case look like
this.setState((prevState, props) => {
return {
favoritesList: [
...prevState.favoritesList,
{"item_name":itemname, "url":url}
]
};
})
For more on setState

Access all children component of a React component

I have react component A which renders a table. Data for one of the columns in the table is rendered through another component B, so <B/> is child of <A/>. I want to perform some action on <B/> whenever user clicks anywhere on the page. This click event listener is defined inside A. How can I loop through all <B/>s from inside class A? My component structure is something like this:
class A extends React.Component {
<B/>
<B/>
<B/>
<B/>
};
I came across React.Children.forEach, but that is useful when children are passed as props via this.props.children; i.e. when the code is something like this:
<A>some markup</A>
const childrenProps = React.Children.map(this.props.children,
(child) => React.cloneElement(child, {
data : this.state,
method : this.method.bind(this)
// here you can pass state or method of parent component to child components
}));
// to access you can use this.props.data or this.props.method in child component
you need to pass this {childrenProps} which include all child components.
So I figured it out. I gave ref to each <B/>, and stored it in an array:
class A extends React.Component {
collectRefs = [];
<B ref={b => {this.collectRefs.push(b)}}/>
<B ref={b => {this.collectRefs.push(b)}}/>
<B ref={b => {this.collectRefs.push(b)}}/>
for(const b of collectRefs) {
// do stuff
}
}

React Redux table update row

I'm new to react and redux.
I have a container which initialize a table component with a list of items, and onclick function.
In the table component I have checkbox for each row. When I click the checkbox I want to select the row (change its style and add selected property to its element model).
When I click on the checkbox I call the onclick property function, then find the item on the list by its id, and change its selected property. The view is not refreshing.
I understand that a component is a "stupid" component that only binds the props and rendering.
What am I doing wrong?
// People container
<Table items={this.props.people} columns={this._columns} onRowSelect={this.selectRow} />
this.selectRow(id){
const selectedLead =_.find(this.props.leads.docs, (lead)=>{
return lead._id == id;
})
selectedLead.selected = !selectedLead.selected;
}
// Table Component - inside render()
{this.props.items.map((item, idx) => {
console.log(item.selected);
return <div style={styles.row(item.selected)}>etc...</div>
})}
Thanks :)
A React Component has props and state.
The difference is, that the Component will never change it props. But it can change it's state. This is why a Component will provide you the setState(...) Method, but no setProps(...) Method.
With that said, your approach to change the selected field in this.props is fundamentally not correct. (There also seems to be another problem in your code where you change the selected field in this.props.leads, but provide this.props.people to the table instead of this.props.leads)
Let me give you a basic example as to how I would solve your problem in Pure React (without a state library like Redux):
const Row = ({ item, onClick }) => (
<tr style={styles.row(item.selected)} onClick={() => onClick(item.id)}>...</tr>
)
const Table = ({ items, onRowClick }) => (
<table>
{items.map(item => <Row item={item} onClick={onRowClick} />)}
</table>
)
class PeopleTable extends React.PureComponent {
constructor(props) {
super(props)
this.state = { people: props.people }
}
componentWillReceiveProps(nextProps) {
if (nextProps.people !== this.state.people) {
this.setState({ people: nextProps.people })
}
}
setItemSelectedState(id) {
this.setState((prevState) => {
const people = prevState.people.map(item => ({
...item,
selected: item.id === id ? !item.selected : item.selected,
})
return { people }
})
}
handleRowClick = (id) => this.setItemSelectedState(id)
render() {
return (<Table items={people} onRowClick={this.handleRowClick} />)
}
}
The things to notice here are:
Row and Table are stateless components. They only take props and return jsx. Sometimes they are also referred to as presentational components.
PeopleTable keeps track of the selected state of each item. This is why it needs state and must be a class.
Because we can't change a components props, we have to keep a reference to props.people in this.state.
componentWillReceiveProps makes sure that if our components receives another list of people, the state is updated accordingly.
setItemSelectedState goes to the root of your problem. Instead of search and update of the item (like in your this.selectRow(id) method), we create a complete new list of people with map and call setState. setState will trigger a rerender of the component and because we created a new people list, we can use the !== check in componentWillReceiveProps to check if people has changed.
I hope this answer was helpful to your question.

How to pass state back to parent in React?

I have a form that has a submit button.
That form calls a function onclick that sets the state of something from false to true.
I then want to pass this state back to the parent so that if it is true it renders componentA but if it is false it renders componentB.
How would I do that in react?
I know I need to use state or props but not sure how to do it. also is this contradicting the one-way flow react principle??
ComponentA code:
<form onSubmit={this.handleClick}>
handleClick(event) {
this.setState({ decisionPage: true });
event.preventDefault();
};
Parent component that controls what it displays:
return (
<div>
{this.props.decisionPage ?
<div>
<LoginPage />
</div>
:
<div>
<Decision showThanks={this.props.showThanks}/>
</div>
}
</div>
)
Move handleClick to the parent and pass it to the child component as a prop.
<LoginPage handleClick={this.handleClick.bind(this)}/>
Now in the child component:
<form onSubmit={this.props.handleClick}>
This way submitting the form will update the state in parent component directly. This assumes you don't need to access updated state value in child component. If you do, then you can pass the state value back from the parent to the child as a prop. One-way data flow is maintained.
<LoginPage handleClick={this.handleClick.bind(this)} decisionPage={this.state.decisionPage}/>
Pass State as a Prop
I have recently learned a method that works great for changing state in a <Parent /> component from a <Child /> component.
This might not be the exact answer for this question, but it is surely applicable to this situation and other similar situations.
It works like this:
set the default STATE in the <Parent /> component - Then add the 'setState' attribute to the <Child />
const Parent = () => {
const [value, setValue] = useState(" Default Value ");
return (
<Child setValue={setValue} />
)
}
Then change the state(in Parent) from the Child component
const Child = props => {
return (
<button onClick={() => props.setValue(" My NEW Value ")}>
Click to change the state
</button>
)
}
When you click the button, the state in the <Parent /> component will change to whatever you set the state to in the <Child /> component, making use of "props".. This can be anything you want.
I Hope this helps you and other devs in the future.
In Parent Component:
getDatafromChild(val){
console.log(val);
}
render(){
return(<Child sendData={this.getDatafromChild}/>);
}
In Child Component:
callBackMethod(){
this.props.sendData(value);
}
Simple Steps:
Create a component called Parent.
In Parent Component create a method that accepts some data and sets
the accepted data as the parent's state.
Create a component called Child.
Pass the method created in Parent to child as props.
Accept the props in parent using this.props followed by method
name and pass child's state to it as argument.
The method will replace the parent's state with the child's state.
Here is an example of how we can pass data from child to parent (I had the same issue and use come out with this )
On parent, I have a function (which I will call from a child with some data for it)
handleEdit(event, id){ //Fuction
event.preventDefault();
this.setState({ displayModal: true , responseMessage:'', resId:id, mode:'edit'});
}
dishData = <DishListHtml list={products} onDelete={this.handleDelete} onEdit={(event, id) => this.handleEdit(event, id)}/>;
At the child component :
<div to="#editItemDetails" data-toggle="modal" onClick={(event)=>this.props.onEdit(event, listElement.id) }
className="btn btn-success">
In React you can pass data from parent to child using props. But you need a different mechanism to pass data from child to parent.
Another method to do this is to create a callback method. You pass the callback method to the child when it's created.
class Parent extends React.Component {
myCallback = (dataFromChild) => {
//use dataFromChild
},
render() {
return (
<div>
<ComponentA callbackFromParent={this.myCallback}/>
</div>
);
}
}
You pass the decisionPage value from the child to the parent via the callback method the parent passed.
class ComponentA extends React.Component{
someFn = () => {
this.props.callbackFromParent(decisionPage);
},
render() {
[...]
}
};
SomeFn could be your handleClick method.
if your parent component is a functional component you can now use the use context way. Which involves passing the ref to the object and the ref to the stateChanging method. What this will allow you to do is change state from parrent in child and also ref tht state while remaining synced with Parent State. You can learn more about this in a youtubeVideo by codedamn titled 'React 16.12 Tutorial 20: Intro to Context API' and 'React 16.12 Tutorial 21: useContext'
This works exactly what I wanted. But in case of set of data with say 50 records with (customer_id, customer_name) as values to be updated from child to parent, then this lags. Do the setState using React.useEffect in child component
i have same problem and so performed this code :
in Parent
const PARENT = () => {
const [value, setValue] = useState("....");
return (
)
}
in Child
const CHILD = props => {
return (
<button onClick={() => props.setValue("....")}>
Click to change the state
</button>
)
}

Categories