filter function doesnt rerender in react - javascript

I've building a page where it get a list of array of object from the server, and I have few checkbox as filter, user will check or uncheck the checkbox, I will have to filter the list, but I have problem rerender the list now. Below is my partial code.
filterItem () => {
// logic goes here
console.log(filteredItems)
}
renderItems (items) => {
return(
//map logic goes here
)
}
render(){
return(
{this.renderItems(this.props.item)}
)
}
how can I rerender renderItems function by passing the filteredItems as param to it? I tried this.renderItems(filteredItems) but did not see my list got updated.

The props or the state of your component does not change, so your component won't get re-rendered. You probably want to use state (See docs here) to store your data and change it accordingly so that your component will re-render.

You can set your items in state of component
constructor(){
super();
this.state = {
items: this.props.items
}
}
and render it with {this.renderItems(this.state.items)}. If i'm clear understand you, and when user click checkbox you filter and change your items array, will enough just setState this new array, and your component will rerender self.

https://facebook.github.io/react/docs/react-component.html
An update can be caused by changes to props or state.
You are not doing either

Related

Why I am getting the old state values after the props change

I just want to understand why in the application I have following situation, below is my constructor of class component:
constructor(props) {
super(props);
this.state = {
tableAlerts: props.householdAlerts,
initialAlerts: props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}
in render function I have:
const { householdAlerts } = this.props;
My issue is that in constructor I got empty array, but in render funtion I have the data. Is it possible to get the data in constructor?
This is a very bad pattern when using the class component. You are ignoring any props updates when you copy the value into state. to manage it:
It requires you to manage two sources of data for the same variable: state and props. Thus, you need to add another render each time your prop change by setting it into state (don't forget to test on equality from prev and next values to avoid being in an infinite loop).
You can avoid setting the state each time your props change by using the getderivedstatefromprops lifecycle method.
So the recommendation is: just use the props; do not copy props into state.
To learn more why you shouldn't, I highly recommend this article.
It is not recommended to set your initial component state in the constructor like so because you gonna lose the ability to use { setState } method after to update this property/state.
The best practice is indeed to refer directly to the prop with { this.prop.householdAlerts }, and keep the state usage for local (or in child components} cases.
if anyhow you want to store props in component state for some reason, call it in lifeCycle -
componentDidMount() {
const { tableAlerts, initialAlerts } = this.props;
this.setState({ tableAlerts, initialAlerts });
}
Hagai Harari is right. Nevertheless, your actual problem seems to be that during your initial rendering the array is empty. Can you ensure that the array has some items, when your component is rendered for the first time?
First rendering -> calls constructor
<YourComponent householdAlerts={[]} />
Second rendering -> updates component
<YourComponent householdAlerts={[alert1, alert2, alert3]} />
If you want initial state to have the prop value.Try something like this with 'this' keyword
constructor(props) {
super(props);
this.state = {
tableAlerts: this.props.householdAlerts,
initialAlerts: this.props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}

React good practice from parent state to children props

So i've been reading a lot lately on react state and props.
My app wasn't that big, but now i'm facing a problem that seems to be commun for a lot of people, and i'm trying to find the best way to implement this.
My app is simple. A SearchBar on top, that display a list of contact. My search bar is a component and is updating a react-redux store with the results of the searchBar value (calling a backend with axios). Till here everything works great.
When the results array is populate (in redux store), my container rerender the results array. Like this:
class Suggestions extends Component {
render() {
console.log('before map: ', this.props.contacts);
const {
contacts,
...rest
} = this.props;
const options = contacts.map((contact, index) => (
<Contact
key={contact.id}
renderToaster={renderToasterFunction}
contact={contact}
/>
));
return <div>{options}</div>;
}
}
const mapStateToProps = (state, props) => ({
contacts: state.contact.results,
});
export default connect(mapStateToProps)(Suggestions);
The problem happen in my Contact component, My list is a lirs of sometimes 10 contacts that are display on the same page. So my problem is that each Contact component need to have it's own state (to add or edit info exemple: if you need to add a new phone number).
//contact component
state = {
contactState: ???
}
...
render(){
//exemple for simplicity
return <div>{this.state.contactState.name}</div>
}
I've founded on react website that it's not a good idea to copy props from parent in state of child. And in my case i've seen it, because if i do this
...
state = {
contactState: this.props.contact <--info from parent
}
first search is ok, but second search with an other letter, results list is not updated and i still see some results of first search.
so i've tried to change my contact component to this:
//contact component
state = {
contactState: ???
}
...
render(){
//exemple for simplicity
return <input value={this.props.contact.name} onChange={this.handleChange}/>
}
And this is working great in term of visual update, all my contact are update even if i do 3-4 searches. But my problem is that, now when i want to edit the name i need to store all my contactState somewhere before saving this and second problem, because my component display {this.props.contact.name} when i edit this, the user can't see the new value, because i can't edit props.
So is there a way to render state from props in a child everytime the parent state change. Or is there a way to 1) save the state when the user edit a contact and 2) display the new value he has written ?
What is the best way when dealing with .map() to have one state foreach children that can be re-renderer when the parent state change and rendering all children with their new state.
Thank you for your help.
Don't hesitate if you need more precisions.
I'm not sure to understand everything but if I get what you want to do:
A simple solution could be to dispatch an action on the onChange
The reducer which catch the action will update your redux store
The props will change and the View too.
But that's will make you dispatch A LOT of actions...
Other option :
Use a state in every Contact-Component which duplicates props
state = {...this.props.contact}
Modify the state on the change handler and use it as value too.
Save and dispatch the "final name" to update redux store and call the api at the same moment to update in on your server
Let me know if that's clear enough

React.js: Changing the child state also changes its proptype passed by parent

The issue I'm having is that when I change the state of the child component, the state of the parent component is also changed. I'm passing the prop to the child as so:
{this.state.users.map((user, index) => (
<UserRow
key={user.id}
user={user}
removeUser={this.removeUser}
getUserInfo={this.getUserInfo}
finishFunc={this.finishChanges}
/>
))}
And then assigning that prop to the a state of the child like this:
state = {rowUser: this.props.user};
However, when I update this child state, it seems that the parent state updates without re-rendering.
I'm displaying the information through the prop, not the state
<div>{this.props.user.rules.length !== 0 ? this.props.user.rules.length : null}</div>
You can see the initial display here
When I click on the "Add" or "Remove" buttons, the state and display changes. You can see the change in the image here
I'm confused on how changing the child's state updates the parent's state without me specifically passing the data back to the parent. I also don't see how it is changing the display since I'm using the prop, not the state.
You can see all of my code in a sandbox here. You can see the permissions number change whenever you click the "add new permission" and "remove permission" buttons, which I don't want to happen.
How can I avoid having the parent state automatically change when I change the child state?
The Problem:
The issue lied in how I was changing the child's state state = {rowUser: this.props.user}. I was mutating the data and resetting the state like this:
addPermission = () => {
const tempUser = { ...this.state.rowUser };
tempUser.permissions.push({
property: "select permission",
info: "type info..."
});
this.setState({ rowUser: tempUser });
console.log("[UserEditingRow.js] permission added");
};
The Solution:
Changing the data directly (i.e. tempUser.permissions.push({...});) was causing a problem. I used React immutability helper to help update the child's state more specifically. So, the new function would be:
addPermission = () => {
this.setState(
update(this.state, {
rowUser: {
permissions: {
$push: [{property: 'select rule', info: ''}],
},
},
}),
);
console.log('[UserEditingRow.js] rule added');
};
Setting the state with immutability helper allowed the child component's state to change without updating the parent component's state and solved my problem.
After reviewing your Sandbox, the issue is you are assigning props.user to state.rowUser and is creating a pointer to the parent components value. This means whenever you change this.state.rowUser it will actually be updating the reference to props.user.
Solution
Use the constructor() to bind the props.user to state.rowUser, this is the recommend approach, additionally use the spread operator to create a new instance of the object passed into the component, this will ensure no pointer is created.
https://reactjs.org/docs/react-component.html#constructor
constructor(props){
this.state = { rowUser: ...props.user }
}
Additionally
Looking quickly at the sandbox I also found a few other anti-patterns that will be causing you some issues.
You are updating state directly Line 76 to 84 in UserRow then calling setState, what you should be doing is using the spread operator to clone the object const tempUserRow = { ...this.state.userRow } then manipulating the object and set it in state via setState. This will ensure your shallow compare in shouldComponentUpdate passes and re-renders your component.
You are using shouldComponentUpdate which is good, but your not doing anything that React.PureComponent does for you out of the box, so you should change extends React.Component to be extends React.PureComponent
https://reactjs.org/docs/react-api.html#reactpurecomponent

React - updating state during render produces errors

I'm new to React and am trying to update the state of a parent component from the child everytime an onChange action happens. The onchange action comes from an input box that when letters are typed it updates the state of searchInputVal with the value of what has been typed. I have a parent <App/> component with the following properties and states here:
updateSampleFilteredState(filteredSamples) {
this.setState({
samples: filteredSamples
});
},
getInitialState () {
return {
samples:allSamples,
searchInputVal:""
}}
I pass the properties and states down to a child component here:
updateNewSampleState(filteredSamples){
return (
this.props.updateSampleFilteredState(filteredSamples)
)
}
render() {
const filteredSamples = this.props.samples.filter(sample => {
return sample.sampleFamily.toLowerCase().indexOf(this.props.searchInputVal.toLowerCase()) !== -1;
});
this.updateNewSampleState(filteredSamples);
return <div className="samples-container-inner-styling">
{
filteredSamples.map((sample) => {
return (...
Before I added the line this.updateNewSampleState(filteredSamples); the child component would render out the filtering just fine but obviously not update the state of sample with the new filtered state. When I the line this.updateNewSampleState(filteredSamples); to execute the function in the component to set the new state I get a list of re-occuring errors that eventually make my app crash. The errors say something about an anti pattern. I'm not sure how else to update the state?
You should't be updating the state from the render function, and you are facing the reason why that's a bad way to do things. Every time you call the setState the component re-renders, so if you call it inside the render function it will be called again and so on... You should ask yourself why are you calling that function there. I guess you could just do it in the onChange function you are using for the input.
As already mentioned by #César, setting the state in the renderer doesn't make sense, since setting the state triggers a rerender of the component, so you basically get something like an infinite render loop.
Given that you are computing filteredSamples only from the props, you could compute that state in the constructor:
The constructor is the right place to initialize state.
However, note the following when deriving state from props in the constructor:
It's okay to initialize state based on props if you know what you're doing. [...]
Beware of this pattern, as it effectively "forks" the props and can lead to bugs. Instead of syncing props to state, you often want to lift the state up.
If you "fork" props by using them for state, you might also want to implement componentWillReceiveProps(nextProps) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.

jquery vs react.js remove item from a list

const RenderItem = (props) => {
return(
<ul id="todo">
{props.items.map((item,i) =>
<li className='list-group-item' data-id={item.id} key={i}>{item.name}
<button className="btn btn-sm btn-primary" onClick={() => props.remove(item.id)}>X</button>
</li>
)}
</ul>
)
};
const TodoItems = React.createClass({
getInitialState() {
return {
items: [
{id:1,name:"Gym"},
{id:2,name:"Jump"},
{id:3,name:"Racing"}
]
}
},
remove(id){
this.setState({
items: this.state.items.filter((el) => id !== el.id)
})
},
render(){
return(
<RenderItem items={this.state.items} remove={this.remove}/>
)
}
})
ReactDOM.render(
<TodoItems />,
document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js"></script>
<script src="https://npmcdn.com/react#latest/dist/react-with-addons.js"></script>
<script src="https://npmcdn.com/react-dom#latest/dist/react-dom.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I'm confused how stuff work here in react.js, I need presduo code how to passing work. I was from jquery background, it's so straight forward, just get the right dom and do $(this).remove(), while in react I'm confused.
<button className="btn btn-sm btn-primary" onClick={() => props.remove(item.id)}>X</button>
When you click what happened? arrow function for? and where does the remove come from?
React follows unidirectional data binding, which means, the data will flow in a single direction. So, the data is stored in a state. Whenever the state changes the component will re-render. A state can be changed at any point in time. But, mostly it will be changed on any action. In your case the action in onClick(). This will call the function will the id of the element which triggered the onClick(). Now, in that function, you will iterate through the state, identify, remove and set the new state using setState() which will re-render the component with the new data. This render will not have the clicked element as it was removed from the state.
The remove() function is passed on to the child component from the parent component which can be accessed using props. This will be a reference to the function declared in the parent component. During the click, using this reference, the function in the parent component will be called!
In react, components are rendered based on their props and state. props are passed from the parent component, and a component may or may not have internal state.
Using jQuery, you would try to remove it by removing the DOM node directly. However, in react, interacting with the DOM directly is an anti-pattern. Since the current state and props determines what is rendered, you want to change one of those that will cause a re-rendering and display something new.
In your case, it looks like you're trying to modify props directly. props are read only and should not be modified. Since props are passed down from the parent component, you would have to change what's being passed down from the parent. There are a few ways you can do that, such as passing a callback function defined in the parent component as a prop that can make such a change. But for this example, it might be easier/more applicable to use state.
Remember, you want to change the component's state to reflect what you want to see (what you want to render). So let's say you have an array called items in your component's state. And let's say you are
class ListDemo extends React.component {
constructor() {
super()
// initialize state
this.state = {
items: ['thing1', 'thing2', 'thing3']
}
}
onClick(index) {
// newItems is a new array with the element at the given index removed
// this logic doesn't handle edge cases, but that's besides the point
const newItems = [
...this.state.items.slice(0, index),
...this.state.items.slice(index + 1)
]
// here we set this component's state's items to
// the new state with what we want removed.
// it will then re-render the component to reflect the new state
this.setState({
items: newItems
})
}
render() {
return (
<div>
{
// here we render buttons for each item in the component's state's items
// we use `onClick` to make it so that clicking the button
// will remove it from the list
this.state.items.map((item, index) => {
return (
<button
onClick={() => this.onClick(index)}
key={`${item}${index}`}
>
{item}
</button>
)
})
}
</div>
)
}
}
Let's go over what is happening in this example. First, when the component is created, it's initial state is set with an items array that contains 3 elements. When it is first rendered, it will create a button for each element in the items array. Notice that what is rendered is purely determined by the state.
Each button also has a callback that is called when it's clicked (set by onClick). This callback is what will remove that specific element from the items array and then update the component's state to have a new items array without the element that was just removed. This in turn causes a re-rendering, which uses the new items array to display the buttons. This time around, the button we removed by click it is no longer there since it's no longer in this.state.items.
That is one of the key concepts of react - components are rendered based on their props and state, and changing either will update what is displayed.
As for why to use an arrow function, it's because it will automatically bind the this value to the ListDemo component. Similarly, you can use something like onClick={this.onClick.bind(this)}. Doing this is necessary since the value of this when clicking the button isn't guaranteed to what you expect. Reading this might make it more clear.

Categories

Resources