React Redux Component passing props not re-render - reactjs

I'm using React with react-redux package and I've created a Redux component.
import React from 'react'
import { connect } from 'react-redux'
class Prova extends React.Component {
constructor(props) {
super(props)
this.state = { ...props }
}
componentDidUpdate(prevProps) {
if (prevProps.errors !== this.props.errors) {
this.setState({
errors: this.props.errors
});
}
}
render() {
const props = this.props.errors ? <div>props: {Object.keys(this.props.errors).map(prop => <div className='props'>{prop}</div>)}</div> : <>NO ERRORS</>
return (
<div>
{props}
</div>
)
}
}
function mapState(state, ownProps) {
return {
}
}
const actionCreators = {
}
const connectedProva = connect(mapState, actionCreators)(Prova)
export { connectedProva as Prova }
I'm calling this component like this:
<Prova errors={this.state.errors} />
Where errors is stored and updated in the state of the Parent component.
The problem is that errors aren't updated in the store state of redux but in the state of the component and this value is passed to the Prova component as "simple" props. But updating the Parent state doesn't re-render the Prova component.
Is it possible that using a Redux component hide the props passed? they are readed only when the Prova component is created the first time.
Thanks
EDIT:
Finally I understood the problem.
In the parent component where I was updating the errors state I wasn't destroying the prev version of errors but filling with the new data.
Now it's working changing it to:
var errors = { ...prevErrors }
Thanks

Related

How to prevent React component mounting if props are empty?

I have a component which works with third party library and I need to add listener after component mounts. For some reason I can't add listeners without data which asynchronously fetched from server and passed through 'connect' function (from react-redux).
How to prevent React component mounting if props are empty?
After some research I haven't found a solution, so I wrote mine:
// No Props No Mount
import React from 'react';
import _ from 'lodash';
function NPNM(WrappedComponent) {
return class extends React.Component {
render() {
const { children } = this.props;
const data = _.omit(this.props, children);
let hasProps = true;
_.forEach(data, elm => {
if (_.isEmpty(elm) && !_.isFunction(elm)) hasProps = false;
});
return hasProps ? <WrappedComponent {...this.props} /> : <></>;
}
};
}
export default NPNM;
It should be used like
connect(mapStateToProps, mapDispatchToProps)(NPNM(YourComponent));

Child component won't re-render

I am using react with redux and I have a todo-list.
I have a 'Todos' component which acts like a container and a 'Todoitem' component which holds every todo.
Everything works fine - reducers change the state and it is updating with new data, but the child component (aka 'Todoitem' component) won't re-render.
Todos.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import TodoItem from "./TodoItem";
class Todos extends Component {
render() {
return (
<div className="Todos">
<div className="todos_title"> {this.props.title} </div>
{this.props.todos.map(todo => {
console.log(todo); // this line prints updated data from state just fine!
return <TodoItem todo={todo} key={todo.id}></TodoItem>;
})}
</div>
);
}
}
// PropTypes
Todos.propTypes = {
todos: PropTypes.array.isRequired
};
const mapStateToProps = state => ({
todos: state.todosReducer.todos
});
export default connect(mapStateToProps)(Todos);
TodoItem.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { checkTodo } from "../actions/todosAction";
class TodoItem extends Component {
onChange = (e, id) => {
console.log(this.props.todo.completed.toString()); // this also prints fine the updated data
this.props.checkTodo(id); // dispatches an action to the reducer to toggle the todo.completed with the right todo.id
};
render() {
console.log('rendering'); // this is the problem - this line calls only in the first rendering, but not when state changes
let { id, title, completed } = this.props.todo;
return (
<div className={completed ? "TodoItemDone" : "TodoItem"}>
<p>
<input
className="todo_cb"
type="checkbox"
onChange={e => this.onChange(e, id)}
checked={completed ? "checked" : ""}
/>
{id}) {title}
</p>
</div>
);
}
}
// PropTypes
TodoItem.propTypes = {
todo: PropTypes.object.isRequired
};
const mapDispatchToProps = dispatch => ({
checkTodo: todo => dispatch(checkTodo(todo))
});
const mapStateToProps = state => ({});
export default connect(
null,
mapDispatchToProps
)(TodoItem);
I noticed that if I do pass a mapStateToProps in child comp it is re-rendering, like this:
const mapStateToProps = state => ({
some_prop: state
});
I understand the if I use mapStateToProps in the child it re-renders but I don't need anything directly from the state in child, the parent does this.
It makes some sense but my todos are stored in an Array in the state and I am mapping over them as you see in the parent component, so I can't map a specific todo from this array to the component props (how could I distinguish each element in the array to map to the prop?).
I am very confused.
I read that component re-renders when state or his props change. Inside the child component the props do change because the parent re-renders and it iterates the todos again and return the component with new props.
Maybe it's not the way to pass the todos to the components but I still don't understand how come the props changes and render() is not called.
Thank you very much!
Edit 1:
I connected the checkTodo action to the parent component and passed the function with props and it works just fine.
Still I don't understand why before the child component haven't re-rendered with the previous code...
Edit 2:
Actually I just lied, it does not work. I forgot to remove mapStateToProps which I said worked, so I am back to square one.
Edit 3:
Solved with by calling forceUpdate(). Still can't understand why it happened.

How to use map on multi objects array in React

This is child component as i can you Props here
Child Component:
import React from "react";
const PeopleList = props => {
console.log("child Props :", props.data);
const list = props.data.map(item => item.name);
return <React.Fragment>{"list"}</React.Fragment>;
};
export default PeopleList;
Main Component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPeople } from "../actions/peopleaction";
import PeopleName from "../containers/peopleName";
class Main extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(fetchPeople());
}
render() {
const { Error, peoples } = this.props;
console.log("data", peoples);
return (
<div className="main">
{"helo"}
<PeopleName data={peoples.results} />
</div>
);
}
}
const mapStateToProps = state => {
return {
peoples: state.peoples.peoples,
error: state.peoples.error
};
};
export default connect(mapStateToProps)(Main);
If i iterate the props multi objects array i can face Map is not define issue;
I need to iterate the props.data multi objects array in child component and i get object from Redux store. once component loaded the redux store.
can you please some one help me on this.
you can find whole code below mentioned
Try this It works in your codesandbox.
{peoples.results && <PeopleName data={peoples.results} />}

Attaching / Detaching Listeners in React

I have a component which, depending on its prop (listId) listens to a different document in a Firestore database.
However, when I update the component to use a new listId, it still uses the previous listener.
What's the correct way to detach the old listener and start a new one when the component receives new props?
Some code:
import React from 'react';
import PropTypes from 'prop-types';
import { db } from '../api/firebase';
class TodoList extends React.Component {
state = {
todos: [],
};
componentWillMount() {
const { listId } = this.props;
db.collection(`lists/${listId}/todos`).onSnapshot((doc) => {
const todos = [];
doc.forEach((t) => {
todos.push(t.data());
});
this.setState({ todos });
});
};
render() {
const { todos } = this.state;
return (
{todos.map(t => <li>{t.title}</li>)}
);
}
}
TodoList.propTypes = {
listId: PropTypes.object.isRequired,
};
export default TodoList;
I've tried using componentWillUnmount() but the component never actually unmounts, it just receives new props from the parent.
I suspect that I need something like getDerivedStateFromProps(), but I'm not sure how to handle attaching / detaching the listener correctly.
Passing a key prop to the TodoList lets the component behave as it should.

Does React allow getting or setting props outside of components

When I have a component inside a component
var MySubComponent;
class MyComponent {
...
getChildProps() {
console.log(MySubComponent.props.nameToGet)
}
...
MySubComponent = withCustomAudio(props => {return(<div>...</div>)});
...
render() {
return(...<MySubComponent {...this.props}/>...)
}
...}
After component render I'd like to get or set the prop of the subcomponent. It returns cant access nameToGet of undefined
Spread attributes is not what I want
(https://zhenyong.github.io/react/docs/transferring-props.html)
No, you won't be able to get/set props like this. It's not the React way. You should read more about thinking in React.
Basically, if you want to change child props in the parent, they should be part of the parent state and passed down to the child as props.
Example:
import React, {Component} from "react";
class Parent extends Component {
state = {
childProp: "Not changed yet"
}
handleChildPropChange = () => {
this.setState({
childProp: "I changed"
})
}
render() {
return <Child myProp={this.state.childProp} onChangeChildProp={this.handleChildPropChange} />;
}
}
class Child extends Component {
render() {
return (
<div onClick={onChangeChildProp}>{this.props.myProp}</div>
)
}
}

Resources