I have an array of persons which I pass as props to to child.
If I delete/add another person to the array the child does update.
However, if I change a property of one of the persons (e.g name) then it's like the child does not notice that the props are different.
Has anyone experienced this issue? I checked that the state on parent updates correctly.
class Parent extends Component {
this.state = {
persons: []
}
componentDidMount() {
this.getPersons(); //This function sets the state of parent
}
getPersons = () => fetch('/persons')
.then(res => res.json())
.then(persons => this.setState({persons: persons}))
.catch(function(error) {
console.log('Could not fetch persons')
})
render() {
<Child persons={this.state.persons}/>
<Child2 getPersons={this.getPersons}/>
}
class Child2 extends Component {
addPerson() {
//Functionality to add person
... this.props getPersons();
}
updatePerson() {
//Functionality to update person
... this.props getPersons();
}
deletePerson() {
//Functionality to delete person
... this.props getPersons();
}
Note: The add/update/delete functionality are all done with a fetch to the API with a POST/PUT/DELETE, respectively. I can verify that the state of Parent updates when I do all 3 things (I can see the name changing of parent state and EVEN on child props it changes but it does not update)
Related
So I'm a beginner with react and I was wondering how to re-render the child after setting the state in the parent (from the child). Here's a code sample. I have a function that calls a GET request using Axios and when I press the button in the child component ideally it will update the state in the parent and also re-render the child but it only does the former.
Parent:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
fetchData = () => {
axios
.get(url)
.then(res => this.setState({data: res.data}))
}
Render() {
return (<Child data={this.state.data} fetchData={this.fecthData}/>)
}
// ...
Child:
class Child extends Component {
// ...
render() {
const { data, fetchData } = this.props
// render data
return <button onClick={fetchData}>Change data then fetch</button>
}
}
Also, are you supposed to make a local state in the Child and set it as a copy of the Parent's state or just passing it down as a prop is okay?
Your parent component holds the data and the child uses it. It seems to me you're doing it the right way. Here is a fully working example:
Codesandbox
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.updateData = this.updateData.bind(this);
}
async fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
return response.json();
}
updateData() {
this.setState({ data: [] }) // Creates a flicker, just so you see it does refresh the child
this.fetchData().then((res) => this.setState({ data: res }));
}
render() {
return <Child data={this.state.data} onAction={this.updateData} />;
}
}
Note I renamed your child prop fetchData into onAction (I don't know what's the name of the action that triggers a refresh, could be onRefresh). It's always best to see components props with separation between data attributes and event attributes.
Even standard components have it this way: <input value={user.firstname} onChange={doSomething} />. So, better to prefix events by on, then the parent decides what to do with it. It's not the child's concern.
class Child extends Component {
render() {
const { data, onAction } = this.props;
return (
<>
<button onClick={onAction}>Change data then fetch</button>
{data.map((item) => (
<div key={item.id}>
{item.id} - {item.title}
</div>
))}
</>
);
}
}
I have a component that I can change how it is rendered based on a prop (added a failed state, and based on whether it fails or not it turns red or stays the original colour), the logic for whether failed is true or false is in the parent component.
I want to change the failed state, but only onBlur (without changing the child component). Is there a way to pass in an onBlur function which applies changes to a child prop?
Ive tried a number of different things like:
Child component
<input
failed={failed}
onBlur={onBlur}
/>
Parent component:
this.props.failed = value;
}
and in the render function:
onBlur={() => this.handleBlur(newValue)}
but it didnt work for me.
Props are data that are passed from a parent to its children and are made available through this.props in the child component.
You maintain whatever prop your are passing to child component either in parent component's state or in redux/flux state (if you have global state management).
When failed is modified, a state change should be triggered on parent component, which in-turn will trigger a re-render inside child component.
For example:
In the following, we pass failed as a prop, and onFailureUpdate function as a callback trigger to child component from parent.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
failed: false
}
}
onFailureUpdate = (value) => {
this.setState({
failed: value
});
}
render() {
return (<ChildComponent failed={this.state.failed} onFailureUpdate={this.onFailureUpdate} />)
}
}
In child component, on blur, we are using the function we passed as prop to modify state in parent, which in-turn will re-render child component.
class ChildComponent extends React.Component {
onBlur = (e) => {
this.props.onFailureUpdate(e.target.value);
}
render() {
return (
<input
value={this.props.failed}
onBlur={(e) => this.onBlur(e)}
/>
)
}
}
Other way:
Or, if there's no necessity for props or parent-child relationship, you can eliminate the need for parent container and go for state maintenance in child.
class RewrittenChildComponentWithState extends React.Component {
constructor() {
this.state = {
failed: false
};
}
onBlur = (e) => {
this.setState({
failed: e.target.value
});
}
render() {
return (
<input
value={this.state.failed}
onBlur={(e) => this.onBlur(e)}
/>
)
}
}
Hope this solves your confusion.
I am injecting the props into the initial state of component. I want to change the if I click on some button inside the component I want to update the state and the data should be reflected immediately.
eventData is an object which contains two attributes one is key(another object) and other is array of object
Data Structure looks like :
eventData--->
{
key:{}
events: [{},{},...]
}
this.state = {
events:this.props.eventData.events,
}
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.eventData.events!==prevState.events){
return { events: nextProps.eventData.events};
}
else return null;
}
componentDidUpdate(prevProps, prevState) {
const {eventData} = this.props;
if(prevProps.eventData.events!==this.props.eventData.events){
//Perform some operation here
this.setState({events: eventData.events});
this.classMethod();
}
}
handleClickButton = (event, action) => {
const {eventData} = this.props;
axios.post(configs.Data.sendEvent, {events: event, key:eventData.key, action:action})//event here is a single event
.then(res=>{
this.setState({events:res.data.singleResult.dataEvent});
});
};
Since I updated the state but the result is not reflected. Can I use componentDidUpdate to update state from upcoming updated state?
You can try to keep incoming data on parent component where props come from without touching componentDidUpdate(). Then pass a function from that component to make changes on state of that component. Then render child component again.
If you want to change both Parent component and Child components data, you can try something like below. It's not a good method but it should work.
class Parent extends Component
{
....
this.state = {dataFromSever:""}
...
changeData = (data) =>
{
this.setState({dataFromServer:data});
}
...
render()
{
...
/*
* ReactDOM.unmountComponentAtNode is goint to unmount react component from
* 'container' component.
* For now, just consider 'container' where you put react components.
*/
ReactDOM.unmountComponentAtNode(document.getElementById('container'));
ReactDOM.render(<ChildComponent changeData={this.changeData}
currentData={this.state.dataFromServer}>
</ChildComponent>,document.getElementById('container'));
...
}
}
and
class ChildComponent extends Component
{
....
...
handleClickButton = (event, action) => {
const {eventData} = this.props;
axios.post(configs.Data.sendEvent, {events: event,
flightTaskKey:eventData.flightKey, action:action})
.then(res=>{
this.props.changeData(res.data.singleResult.dataEvent);
});
};
...
render()
{
... <...> this.props.currentData </...>
}
}
I have a parent component which contains a function, which when called needs to acces the childrenĀ“s component state. I dont want to move the whole state to the parent component because i want the children component to be independent. What is the cleanest and most recommended way to achieve this?
class ParentComponent extends Component {
render() {
return (
<div>
<ChildComponent/>
<SaveButton onClick={this.saveFunction}/>
</div>
)
}
saveFunction = () => {
//Here i need to acces the child Component state
}
}
My solution so far was that everytime something changed in child component i called a function which was passed from the parent Component. Like this:
class ChildrenComponent extends Component {
state = {
name: "David",
age: 19
}
render() {
return (
//inputs with the inputChange function
)
}
inputChange = (e) => {
//Update the state
//Then pass the state to the parent
this.props.passStateToParent(this.state)
}
}
I would recommend to look up some of the React patterns - especially Render Props, as it allows to expose the state and wanted methods of a component - what you want in this situation.
Best of luck!
You can make a function in parent component and pass it down to child component as prop. This function could return to parent component the state of your child component. See more here: https://reactjs.org/docs/faq-functions.html
You cannot directly access the state of the child component,this can be done by passing the state to methods of parent component which are passed as props to child component,the following example demonstrate how to do it .
class ParentComponent extends React.Component {
somefunc() {
//do your action
}
render() {
<ChildComponent parentfunc={this.somefunc}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {somedate:'value'}
this.func = this.func.bind(this)
}
func() {
this.props.parentfunc(this.state)
}
render() {
<button onClick={this.func}>text</button>
}
}
The situation is this: i have a database, where the data is. The data is structured with an array of objects. The objects have three properties (which are relevant now). These are: id, parentId, and status. The components build up with these properties, and the component clones itself recursively, so they are nested within each other. It looks like this:
class Task extends React.Component{
constructor(props){
super(props);
this.state = {
status: this.props.status
}
}
//gets fired right after the state changed by removeTask
static getDerivedStateFromProps(props){
console.log("state from props")
return{status: props.status}
}
componentDidUpdate(){
console.log("update");
this.checkDeleted()
}
checkDeleted = () => {
if (this.state.status === 'deleted'){
deleteTask(this.props.id) //a function which deletes from database
}
}
tasks = () => {
console.log("childs rendered")
return this.props.tasks
.filter(task => task.pId === this.props.id)
.map(task => {
return <Task
id={task.id}
pId={task.pId}
status={this.state.status}
/>
})
}
removeTask = () => {
console.log("state changed")
this.setState({status: 'deleted'})
}
render(){
console.log("render")
return(
<div
<button onClick={this.removeTask} />
{this.tasks()}
</div>
)
}
}
What happens: the order of the logs are the next:
state changed (removeTask())
state from props (gDSFP())
render
childs rendered (tasks() fired inside render())
update (componentDidUpdate())
This isn't good, because when the state changed from removeTask(), it gets right back from the props with gDSFP, before the component can pass the changed state to it's childs. But i want to set the state from the props, because the childs need to get it. What could happen here is: the removeTask() fired, sets the new state, rerender, the childs get the new status as a prop, and when the update happens, deletes all the component, and it's child from the database. So what will be good:
click happened, set new state
render
render childs, set they status prop to the state
check if the status is "deleted"
set state from props if it's changed, and rerender
How to earn this?
I have problem with your order to begin with. Your data depends on whats in the DB. You might delete from the state and the DB task failed. So why bother updating the state manually. Just listen and load your state from props that come from DB. when you delete from the DB, your props will be updated and re-render will occur. So basically if i were you , i would stick with
class Task extends React.Component{
constructor(props){
super(props);
}
//gets fired right after the state changed by removeTask
static getDerivedStateFromProps(props){
console.log("state from props")
return{status: props.status}
}
componentDidUpdate(){
console.log("update");
}
tasks = () => {
console.log("childs rendered")
return this.props.tasks
.filter(task => task.pId === this.props.id)
.map(task => {
return <Task
id={task.id}
pId={task.pId}
status={this.props.status}
/>
})
}
removeTask = () => {
console.log("state changed");
deleteTask(this.props.id) //a function which deletes from database
}
render(){
console.log("render")
return(
<div
<button onClick={this.removeTask} />
{this.tasks()}
</div>
)
}
}
from the above, you can notice i removed checkDeleted because i don't need to update my state. I can just rely on props. Remove set state status because i can just rely on props sttaus which btw tally or is in sync with DB.