How to update redux store when calling a push method - javascript

I am using FieldArray , my requirement is add upto 3 elements and I am using push method for this. I have a redux variable/property to check whether elements have reached upto 3.But how I can achieve that store variable change when I do push an element(pushing an element into array should increase that property value.
{fields.map((doc, index) => (
<div>
<h4>Element #{index + 1}</h4>
<br/>
<div className="row">
{fields.length < 3 &&
<button type="button" className="btn btn-primary" onClick=
{() => fields.push({})}>
Add Element
</button>
</div>
</div>))}
Any pointers will be helpful, Thank you.

The only way to update redux store is to dispatch actions. Here, when you click on your button, you just execute a function that updates your current fields array.
Second point, when working on React and Redux, you need to modify your state immutably, without changing it, by passing a new object. For example :
state = {
cars: []
}
const newState = {
...state,
cars: ['Ferrari']
}

Related

Returning value from React child component

I am beginner in React so I apologize if this is a basic question. I am trying to build an online Sudoku grid using ReactJS but I am unable to render the digits in the field. I have made two components one being the Sudoku.js which renders the complete sudoku block and other being Node.js which consists of an input field wrapped inside a div. the functionality I want is to change the default value of the input field (which is "" in my case) whenever the user types a number. I have tried the below approach. It updates the value of my grid variable but does not show the updated value in the input field of Node. Kindly help.
Sudoku.js
const sudokugrid = (
<div>
{grid.map((row,rowIdx) => {
return (
<div key = {rowIdx} className="rowWrapper">
{row.map((col,colIdx) => {
var element = grid[rowIdx][colIdx].value;
const change = (event) => {
element = event.target.value;
grid[rowIdx][colIdx].value = element;
setGrid(grid);
return element;
}
return (
<Node
key = {colIdx}
onChangeValue = {change}
value = {element}
/>
)
})}
</div>
)
})}
</div>
);
here grid is a 2D array of 9x9 elements which have initial value of all the elements being "" , which are supposed to be updated when the user types in the value in the respective fields. the problem is that the value is updated when the user types the number but the number is not shown in the input field
The Node component is as follows:
function Node(props){
return (
<div className="box">
<input
className = "num"
type="number"
value = {props.value}
onChange = {props.onChangeValue}
/>
</div>
)
}
This is because react can't detect mutations. See this article. You have to store the sudoku grid in a state value and change the state with a callback function.
An example.
I suggest using an object map for storing the state because you can manage the values a lot easier.

How to immutable update an object given that we have to pass the same object as props?

I have an object amounts that gets updated on button clicks. Ans i pass that object as a prop to another component. What i am doing right now is updating object in mutable way on button click event.
onClick = e => {
amounts.map(
amount => (amount.tax = taxes ? 500 : 0)
);
}
<Display amounts={amounts} />
How can i update amounts in an immutable way?
As mentioned in the comments, there are a few things going on:
You are not updating the amounts Array reference, so React will not re-render based on this mutation.
You are using Array#map to update a single property. This will update the Object reference in the amounts collection.
There is no setAmounts or anything similar in order to update the value of the amount property in a parent component.
Assuming you are using useState in the <Display />s parent component, you will have to pass the setAmounts function to the <Display /> component using props.
<Display amounts={amounts} setAmounts={setAmounts} />
onClick = e => {
setAmounts(
amounts.map(
amount => ({ ...amount, tax: taxes ? 500 : 0 })
);
);
}

How to toggle two different buttons (function/text) based on external condition (e.g. num of items purchase)

I am revisiting an earlier idea to toggle between two buttons conditionally in a CRA.
import ...
const ...
export const Index = () => {
// Boolean to toggle buttons conditionally
const [reachMax] = React.useState( id <= 8 );
return (
<div>{(reachMax &&
<div{(reachMax &&
<button
id="btn1"
type="button"
className="..."
onClick={event}
>
Event A: "Sales!"
</button>
</div>)
||
<div>
<button
id="btn2"
type="button"
className=" "
disabled='true'
>
Event B: "Out of Stock"
</button>
</div>)
}
)
}
Using a state hook. The condition for the Boolean ( contract.tokenUri.id <= 8 ) is taken from an external smart contract dynamically. How can the condition be set so that it would not return an undefined error?
You're trying to access this in an arrow function. Besides not knowing what this will point to, in React that's not how you access props in functional components.
I assume you use Index like this in your Toggle component:
<Index data={...} />
Then you access it like:
export const Index = (props) => {
const data = props.data;
// ....
};
Managed to resolve by setting this condition in order to read the ID off the smart contract:
const reachMax = mintedState.state === "SUCCESS" && mintedState.data.length <= 8;
There's no need for the constructor as the state is directly passed into the app from the external contract. Once the nmintedState.state condition is satisfied (success), the data.length (a proxy for the ID) can be used as the final condition for setting the right button.
This problem is specific to the combination of Solidity contract and ReactJS used.

React - sort array of child components with state

Currently I'm working on a react project, but I'm seeing some unexpected behavior when sorting an array of stateful child components.
If I have a parent component
export function Parent(){
const [children, setChildren] = useState([
{name:'Orange',value:2},
{name:'Apple',value:1},
{name:'Melon',value:3}
])
var count = 0
function handleSort() {
var newChildren=[...children]
newChildren.sort((a,b)=>{return a.value-b.value})
setChildren(newChildren)
}
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map((child) => {
count++
return(<ChildComp key={count} details={child}/>)
})}
</div>
)
}
And a child component
function ChildComp(props){
const[intCount,setIntCount] = useState(0)
function handleCount(){
setIntCount(intCount+1)
}
return (
<div>
<p>{props.details.name}</p>
<button onClick={handleCount}>{intCount}</button>
</div>
)
}
When the page first renders everything looks great, three divs render with a button showing the number of times it was clicked and the prop name as it was declared in the array. I've noticed that when I sort, it sorts the props being passed to the child components which then rerender, but the intCount state of the child component stays tied to the original location and is not sorted. is there any way to keep the state coupled with the array element through the sort while still maintaining state data at the child level, or is the only way to accomplish this to raise the state up to the parent component and pass a callback or dispatch to the child to update it?
The count is not is not sorted. It just got updated when you sorted.
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity
Every time you sort, key stay the same, as you use count.
Try using value as key
export function Parent(){
// ....
return (
<div>
<button onClick={handleSort}>Sort</button>
{children.map(child => {
return <ChildComp key={child.value} details={child}/> // key is important
})}
</div>
)
}
More info: https://reactjs.org/docs/lists-and-keys.html#keys

Dynamically created custom form components in react

See this gist for the complete picture.
Basically I will have this form:
When you click the plus, another row should appear with a drop down for day and a time field.
I can create the code to add inputs to the form, however I'm having trouble with the individual components (selectTimeInput is a row) actually updating their values.
The onChange in the MultipleDayTimeInput is receiving the correct data, it is just the display that isn't updating. I extremely new to react so I don't know what is causing the display to not update....
I think it is because the SelectTimeInput render function isn't being called because the passed in props aren't being updated, but I'm not sure of the correct way to achieve that.
Thinking about it, does the setState need to be called in the onChange of the MultipleDayTimeInput and the input that changed needs to be removed from the this.state.inputs and readded in order to force the render to fire... this seems a little clunky to me...
When you update the display value of the inputs in state, you need to use this.setState to change the state data and cause a re-render with the new data. Using input.key = value is not the correct way.
Using State Correctly
There are three things you should know about
setState().
Do Not Modify State Directly
For example, this will not re-render a
component:
// Wrong
this.state.comment = 'Hello';
Instead, use setState():
// Correct
this.setState({comment: 'Hello'});
The only place where you
can assign this.state is the constructor.
read more from Facebook directly here
I would actually suggest a little bit of a restructure of your code though. It's not really encouraged to have components as part of your state values. I would suggest having your different inputs as data objects in your this.state.inputs, and loop through the data and build each of the displays that way in your render method. Like this:
suppose you have one input in your this.state.inputs (and suppose your inputs is an object for key access):
inputs = {
1: {
selectedTime: 0:00,
selectedValue: 2
}
}
in your render, do something like this:
render() {
let inputs = Object.keys(this.state.inputs).map((key) => {
let input = this.state.inputs[key]
return (<SelectTimeInput
key={key}
name={'option_' + key}
placeholder={this.props.placeholder}
options={this.props.options}
onChange={this.onChange.bind(this, key)}
timeValue={input.selectedTime}
selectValue={input.selectedValue}
/>)
)}
return (
<div>
<button className="button" onClick={this.onAddClick}><i className="fa fa-plus" /></button>
{ inputs }
</div>
);
}
Notice how we're binding the key on the onChange, so that we know which input to update. now, in your onChange function, you just set the correct input's value with setState:
onChange(event, key) {
this.setState({
inputs: Immutable.fromJS(this.state.inputs).setIn([`${key}`, 'selectedTime'], event.target.value).toJS()
// or
inputs: Object.assign(this.state.inputs, Object.assign(this.state.inputs[key], { timeValue: event.target.value }))
})
}
this isn't tested, but basically this Immutable statement is going to make a copy of this.state.inputs and set the selectedTime value inside of the object that matches the key, to the event.target.value. State is updated now, a re-render is triggered, and when you loop through the inputs again in the render, you'll use the new time value as the timeValue to your component.
again, with the Object.assign edit, it isn't tested, but learn more [here]. 2 Basically this statement is merging a new timeValue value in with the this.state.inputs[key] object, and then merging that new object in with the entire this.state.inputs object.
does this make sense?
I modified the onChange in the MultipleDayTimeInput:
onChange(event) {
const comparisonKey = event.target.name.substring(event.target.name.length - 1);
const input = this.getInputState(comparisonKey);
input.selected = event.target.value;
input.display = this.renderTimeInput(input);
let spliceIndex = -1;
for (let i = 0; i < this.state.inputs.length; i++) {
const matches = inputFilter(comparisonKey)(this.state.inputs[i]);
if (matches) {
spliceIndex = i;
break;
}
}
if (spliceIndex < 0) {
throw 'error updating inputs';
}
this.setState({
inputs: [...this.state.inputs].splice(spliceIndex, 1, input)
});
}
The key points are:
// re render the input
input.display = this.renderTimeInput(input);
// set the state by copying the inputs and interchanging the old input with the new input....
this.setState({
inputs: [...this.state.inputs].splice(spliceIndex, 1, input)
});
Having thought about it though, input is an object reference to the input in the this.state.inputs so actually [...this.states.inputs] would have been enough??

Categories

Resources