Component state variable as img src url - javascript

I am new to React. I am trying to set the src attribute of an image using a component state variable. But when I run the code I don't see the component getting rendered. However if I explicitly define src url of the image, it works.
My code is as follows. I am also using React carbon component and react grid layout.
import 'carbon-components/scss/globals/scss/styles.scss';
import GridLayout from 'react-grid-layout';
import React, { Component } from 'react';
import { ClickableTile,Link } from 'carbon-components-react';
class ct extends React.Component {
constructor() {
super()
this.state ={
arr:[]
}
}
componentWillMount(){
let tilesData = []
fetch("https://xyz/api/abc")
.then((results)=>{
return results.json();
}).then((data)=>{
let details = {
imageUrl:data.images["image"]
}
tilesData.push(details)
this.setState({arr : tilesData})
})
render() {
var layout = [
{i: 'a', x: 0, y: 0, w: 2, h: 2}
];
return (
<GridLayout className="layout" layout={layout} rowHeight={30} width={50}>
<ClickableTile key="a">
<div>
// issue here ---------VVVVV
<img src={this.state.arr[0].imageUrl}/>
</div>
</ClickableTile>
</GridLayout>
);
}
}
What could be the issue?
UPDATE - I fixed the issue
FIX - this.state ={
arr:[{}]
}
}

Your fetch operation is asynchronous. You are changing the state without waiting for it to finish. You need to set the state in the fetch success callback for the setstate to take effect. Thus, your state is empty because the state was never changed.
Try this:
fetch("https://xyz/api/abc")
.then((results)=>{
return results.json();
}).then((data)=>{
let details = {
imageUrl:data.images["image"]
}
tilesData.push(details)
this.setState({arr : tilesData}) //call setState in the fetch callback
})

I fixed it
this.state =
{
arr:[{}]
}
}

Related

React-select defaultValues

i have a select menu with defaultValue is null
when i pass props to it , it dosent rerender with the new props as defaultValues
ps : the select is multi
i tried to use component will recieve props and everything that i find but still dosent work
this is my select component :
import React, { useState, useEffect } from "react";
import Select from "react-select";
class SelectMenu extends React.Component {
state = {
defaultValues: [],
};
componentWillReceiveProps(newProps) {
this.setState({ defaultValues: newProps.defaultValue });
}
render() {
return (
<Select
options={this.props.options}
closeMenuOnSelect={this.props.closeMenuOnSelect}
components={this.props.components}
isMulti={this.props.isMulti}
onChange={(e) => this.props.onChange(e, this.props.nameOnState)}
placeholder={this.props.default}
defaultValue={this.state.defaultValues}
/>
);
}
}
export default SelectMenu;
componentWillReceiveProps won't be called during mounting.
React doesn’t call UNSAFE_componentWillReceiveProps() with initial props during mounting. It only calls this method if some of component’s props may update. (https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops)
Also, componentWillReceiveProps is deprecated and will be removed in React 17. Take a look at getDerivedStateFromProps instead, and especially the notes on when you do not need it.
I beleive that in your case using the constructor will be perfectly fine, something like:
class Components extends React.Component {
constructor(props) {
super(props)
this.state = { some_property: props.defaultValue }
}
}
i find a solution for this problem
by using components will recieve props
and setting my state with the comming props
and in the render you need to do condition to render the select menu only if the state.length !== 0
i posted this answer just in case someone face the same problem i know its not the most optimal solution but it works for me
sorry for the previous solution but its not optimal i find a way to make it work
so instead of defaultvalues
you have to make its as value props
and if you want to catch the deleted and added values to your default
this function will help you alot
onChange = (e) => {
if (e === null) {
e = [];
}
this.setState({
equipments: e,
});
let added = e.filter((elm) => !this.state.equipments.includes(elm));
if (added[0]) {
let data = this.state.deletedEquipments.filter(
(elm) => elm !== added[0].label
);
this.setState({
deletedEquipments: data,
});
}
let Equipments = e.map((elm) => elm.label);
let newEquipments = Equipments.filter(
(elm) => !this.state.fixed.includes(elm)
);
this.setState({
newEquipments: newEquipments,
});
let difference = this.state.equipments.filter((elm) => !e.includes(elm));
if (difference.length !== 0) {
if (
!this.state.deletedEquipments.includes(difference[0].label) &&
this.state.fixed.includes(difference[0].label)
) {
this.setState({
deletedEquipments: [
...this.state.deletedEquipments,
difference[0].label,
],
});
}
}
};
constructor(props) {
super(props);
this.state = {
equipments: [],
newEquipments: [],
deletedEquipments: [],
};
}

How to change the value in Constructor ReactJs

My problem is that the code is working correctly
I would like to be able to change the value val: 'yolo' by either a component from another page or direct by my database
Do you have an idea, how to fix this? Neff
import React from 'react'
import axios from 'axios'
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT + '/api';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
clickHandler(){
this.setState({currentPosition: (this.state.currentPosition + 1)%this.state.totalLength})
}
render() {
return (
<div >
<button onClick={this.clickHandler} >Move to the Right</button>
{
Array.from(
{length: this.state.totalLength},
(_,i) => (
<div key={i} className="slot">
<p>{i === this.state.currentPosition ? this.state.val : null}</p>
</div>
)
)
}
</div>
)}
}
export default App;
one way you can change the value of Yolo similar way as you are getting data from the server.
as for changing it from another component , you can do it by either getting it as a props from its parent component where you use this component
<App yoloVal = {"yoloValue"}/>
and you can receive it in props either when it mounts or when it updates
componentDidMount(){
this.setState({
yolo : this.props.yoloVal
}
}
or when it updates
componentDidUpdate(){
if(this.props.yoloVal !== prevProps.yoloVal){
this.setState({
yolo : this.props.yoloVal
}
}
}
you can also get this value from a child in the App by passing it a method
write a method in the App Component
setYoloValue(val){
this.setState({
yolo : val
}
}
now pass this method in render method of App to a child component
return (
<ChildComponent setYoloValue = {this.setYoloValue.bind(this)}
)
we are using bind so when this method is called the context remains of the parent instead of the caller(child component)
now you can use this method anywhere in the child to set the value of Yolo on parent
class ChildComponent extends Component {
componentDidMount(){
this.props.setYoloValue("new Yolo Value by child")
}
}
Now as for passing data between siblings , you can give the data by using the above two methods , first have a common parent , pass the data to parent by using second method then pass that data parent received to the other children as the first method. that is how you can acheive communication between siblings components.
as for setting the value from any other component in the app that is not directly related to you component , you need Redux or similar that does the job for you by keeping the values in a common store and components listen to that store and receive the update when the value in the store updates.
I would like to be able to change the value val: 'yolo'
1.by either a component,
2.from another page
3.or direct by my database
i'm actually surprised by the following piece of code, and not even sure, it 's a valid one. you are initializing this.state twice inside your constructor.
constructor(props) {
super(props);
--> this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
--> this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
}
you initialize your entire variables inside your constructor..
constructor(props) {
super(props);
this.state = {
data: [],
currentPosition: 0,
totalLength: 3,
val: 'yolo',
};
this.clickHandler = this.clickHandler.bind(this)
}
idea is to pass a function(prevState) as a callback to update the local state so as to escape batching.
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState(prevState => ({
...prevState,
data: res.data,
}))
}
i'm not sure this will work as you expected..
clickHandler(){
this.setState({currentPosition: (this.state.currentPosition + 1)%this.state.totalLength})
}
since you are doing a division, it's good to Math.floor or ceil(you need to find whichever value meets your requirement.)
//1. by a component..
handleValChange(val) => {
this.setState(prevState => ({
...prevState,
val,
}))
}
//now u can pass it to a child component.
render() {
const { handleValChange } = this
return (
<div>
<...rest of the div.../>
<ChildComponent {...{ handleValChange }} />
</div>
)
}
from another page.
from another page means, probable a diffrent route. in such cases u need to update this globally(redux, mobx etc..) and the value should also live globally not locally. u can pass id's and stuff via url but function, not possible.
direct by db.
this is where u make an api call and based on the response u update the state. that means, it's time to extract your application into a global state(redux, mobx etc..)
this.state = {
data: [],
};
this.clickHandler = this.clickHandler.bind(this)
this.state = {currentPosition: 0, totalLength: 3, val: 'yolo'}
You should not have two states in one constructor. Change it to one state:
this.state {
data: [],
currentPosition: 0,
totalLength: 3,
val: 'yolo',
}
As for changing the value from another component, there are two easy solutions.
1) Using Redux to handle state, instead of local state, probably the best solution.
2) Use a callback function that call setState in that component, and pass it to the other component, if it is a child of this component.
const myCallbackFunction(value: string) {
this.setState({ val: value })
}

set multiple states, and push to state of array in one onClick function

I'm running into a recurring issue in my code where I want to grab multiple pieces of data from a component to set as states, and push those into an array which is having its own state updated. The way I am doing it currently isn't working and I think it's because I do not understand the order of the way things happen in js and react.
Here's an example of something I'm doing that doesn't work: jsfiddle here or code below.
import React, {Component} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
}
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
setCategoryStates = (categoryTitle, categorySubtitle) => {
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
render() {
return (
<CategoryComponent
setCategoryStates={this.setCategoryStates}
categoryTitle={'Category Title Text'}
categorySubtitle={'Category Subtitle Text'}
/>
);
}
}
class CategoryComponent extends Component {
render() {
var categoryTitle = this.props.categoryTitle;
var categorySubtitle = this.props.categorySubtitle;
return (
<div onClick={() => (this.props.setCategoryStates(
categoryTitle,
categorySubtitle,
))}
>
<h1>{categoryTitle}</h1>
<h2>{categorySubtitle}</h2>
</div>
);
}
}
I can see in the console that I am grabbing the categoryTitle and categorySubtitle that I want, but they get pushed as null into this.state.categoryArray. Is this a scenario where I need to be using promises? Taking another approach?
This occurs because setState is asynchronous (https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly).
Here's the problem
//State has categoryTitle as null and categorySubtitle as null.
this.state = {
categoryTitle: null,
categorySubtitle: null,
categoryArray: [],
}
//This gets the correct values in the parameters
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
})
this.pushToCategoryArray();
}
//This method is using the state, which as can be seen from the constructor is null and hence you're pushing null into your array.
pushToCategoryArray = () => {
this.state.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
Solution to your problem: pass callback to setState
setCategoryStates = (categoryTitle, categorySubtitle) => {
//This is correct, you're setting state BUT this is not sync
this.setState({
categoryTitle: categoryTitle,
categorySubtitle: categorySubtitle,
}, () => {
/*
Add state to the array
This callback will be called once the async state update has succeeded
So accessing state in this variable will be correct.
*/
this.pushToCategoryArray()
})
}
and change
pushToCategoryArray = () => {
//You don't need state, you can simply make these regular JavaScript variables
this.categoryArray.push({
'categoryTitle': this.state.categoryTitle,
'categorySubtitle': this.state.categorySubtitle,
})
}
I think React doesn't re-render because of the pushToCategoryArray that directly change state. Need to assign new array in this.setState function.
// this.state.categoryArray.push({...})
const prevCategoryArray = this.state.categoryArray
this.setState({
categoryArray: [ newObject, ...prevCategoryArray],
)}

React state with calculated fields

I have a react component, which has properties and state. Some fields of state contain input data (uplifted from input control), but there is also fields in the state that must be Calculated based on current State and Props:
The question: what is the best way to update calculated fields of the state (based on other fields of state and props)?
Ugly way to do it:
componentDidUpdate(){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
In this case I get infinite loop of updates or in the best case (if I use PureComponent) double rendering invocation.
The best solution I found so far (but still ugly):
Is to create a calculated object in state, which contains calculated fields and updated in componentWillUpdate avoiding setState:
componentWillUpdate(nextProps,nextState){
nextState.calculated.field1=f(nextProps,nextState)
}
class ParentComponent extends React.Component {
constructor(props, ctx) {
super(props,ctx)
this.state={A:"2"}
}
render() {
console.log("rendering ParentComponent")
return <div>
<label>A=<input value={this.state.A} onChange={e=>{this.setState({A:e.target.value})}} /></label> (stored in state of Parent component)
<ChildComponent A={this.state.A} />
</div>
}
}
class ChildComponent extends React.PureComponent {
constructor(props,ctx) {
super(props,ctx);
this.state={
B:"3",
Calculated:{}
}
}
render() {
console.log("rendering ChildComponent")
return <div>
<label>B=<input value={this.state.B} onChange={e=>{this.setState({B:e.target.value})}} /></label> (stored in state of Child component state)
<div>
f(A,B)=<b>{this.state.Calculated.result||""}</b>(stored in state of Child component)
<button onClick={e=>{ this.setState({Calculated:{result:new Date().toTimeString()}}) }}>Set manual value</button>
</div>
</div>
}
componentWillUpdate(nextProps, nextState) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, nextState.B)
}
componentWillReceiveProps(nextProps) {
this.state.Calculated.result = getCalculatedResult(nextProps.A, this.state.B)
}
componentWillMount() {
this.state.Calculated.result = getCalculatedResult(this.props.A, this.state.B)
}
}
function getCalculatedResult(a,b) {
const aNum = Number(a)||0
const bNum = Number(b)||0;
const result = (aNum*bNum).toString();
return result;
}
ReactDOM.render(<ParentComponent/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
This is also ugly solution and React does not recommended to mutate state avoiding setState. So what is right solution for that?
NOTE:
In my real application I cannot recalculate f(a,b) every single time during rendering, because it's actually complex object, so I need to cache it somehow and the best way is in the state.
If you are using React 16.8.0 and above, you can use React hooks API. I think it's useMemo() hook you might need. For example:
import React, { useMemo } from 'react'
const MyComponent = ({ ...props }) => {
const calculatedValue = useMemo(
() => {
// Do expensive calculation and return.
},
[a, b]
)
return (
<div>
{ calculatedValue }
</div>
)
}
For more details, refer to the React documentation
I wouldn't advice you to store your calculated value inside your state. My approach would be more like this:
import PropTypes from 'prop-types';
import React from 'react';
class Component extends React.Component {
static defaultProps = { value: 0 };
static propTypes = { value: PropTypes.number };
state = { a: 0, b: 0 };
result = () => this.state.a + this.state.b + this.props.value;
updateA = e => this.setState({ a: +e.target.value });
updateB = e => this.setState({ b: +e.target.value });
render() {
return (
<div>
A: <input onChange={this.updateA} value={this.state.a} />
B: <input onChange={this.updateB} value={this.state.b} />
Result: {this.result()}
</div>
);
}
}
The problem with storing the calculation inside your state is, that the calculation can be mutated by multiple sources. If you use my solution, there is no way, that anything can overwrite the calculation WITHOUT using the correct function to calculate them.
You can save calculated result in this.calculated instead of this.state. It is dependent data. All data which causes update and render is already in state and props.
class Component extends React.Component {
constructor(props) {
super(props)
state = {
b: 0
}
}
updateThis = (event) => {
this.setState({ b: event.target.value });
}
componentWillUpdate(nextProps,nextState){
this.calculated.field1=f(nextProps.a, nextState.b)
}
render() {
return (
<form>
A = <input onChange={this.props.updateParent} value={this.props.a} /> <br>
B = <input onChange={this.updateThis} value={this.state.b} /> <br>
f(A,B) = {this.calculated.field1} <br>
</form>
);
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props)
state = {
a: 0
}
}
render() {
return (
<Component
updateParent={event=>this.setState({a: event.target.value})}
a={this.state.a}
/>
}
}
}
You're first attempt is the right way to solve this problem. However, you need to add a check to see if state has actually changed:
componentDidUpdate(prevProps, prevState){
if(prevState.field !== this.state.field){
this.setState({calculatedField:calculate(this.props,this.state)}))
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.calculatedField !== nextState.calculatedField
}
You need to check the pieces of state and props that you use in your calculate method and make sure they have changed before updating state again. This will prevent the infinite loop.
It looks like the "state" is the place to store everything (even computed values) you'll need to use on the render function, but usually we have the problem you describe.
Since React 16.3 a new approach for this situation has been given in the way of the static getDerivedStateFromProps (nextProps, prevState) "lifecycle hook".
You should update at least to this version if you haven't, and follow the advice given by the React Team on their blog.
Here is the official documentation for this functionality.
The issue here is that this function is invoked before every render, and being "static" you cannot access the current instance previous props, which is usually needed to decide if the computed value must be generated again or not (I suppose this is your case, as you have stated your computation process is heavy). In this case, the React team suggests to store in the state the values of the related props, so they can be compared with the new ones:
if (nextProps.propToCompute !== prevState.propToComputePrevValue) {
return {
computedValue: Compute(nextProp.propToCompute),
propToComputePrevValue: nextProps.propToCompute
};
}
return null;
Do not include redundant information in your state.
A simplified example is having firstName and lastName in your state. If we want to display the full name in your render method, you would simply do:
render() {
return <span>{`${this.state.firstName} ${this.state.lastName}`}</span>
}
I like this example because it's easy to see that adding a fullName in our state, that just holds ${this.state.firstName} ${this.state.lastName} is unnecessary. We do string concatenation every time our component renders, and we're okay with that because it's a cheap operation.
In your example, your calculation is cheap so you should do it in the render method as well.

ReactJS How to add additional image To props and change them on Click

Hello im trying to add an second image to my image props and change them on click any suggestions how can i solve it? here is my bin
my react bin
Your image src needs to be listening for a state and not a props. Props is immutable and State is mutable.
Below is an example of how you can do this.
import React from 'react'
import { render } from 'react-dom'
import { CSSTransitionGroup } from 'react-transition-group'
class FadeImage extends React.Component
{
constructor(props, context, images)
{
super(props);
this.state = {
index: 0,
imageClass: 'image'
};
this.fadeImage = this.fadeImage.bind(this);
}
fadeImage(e)
{
e.preventDefault();
var s = {
index: (this.state.index + 1) % this.props.images.length
};
if (this.state.imageClass === 'image')
{
s.imageClass = "image-loaded";
}
else
{
s.imageClass = "image";
}
this.setState(s);
console.log('clicked');
}
render()
{
return (
<div>
<a href="#" onClick={this.fadeImage}>Change Me!</a>
<img src={this.props.images[this.state.index]} />
</div>
)
}
}
render(<FadeImage images={['http://via.placeholder.com/350x150', 'http://via.placeholder.com/350x151']} />, document.querySelector('#app'))
Note that its perfectly fine to keep image paths in props. But you do need to have a current image index in state. Also there is no need to call setState multiple times. You can prepare your updates as part of one object and introduce all of them at once.
You need to pass an array of images as props.
render(<FadeImage images={["http://via.placeholder.com/350x150", "http://via.placeholder.com/350x151"]} />, document.querySelector('#app'))
And you update piece of state (currentImage) that doesn't exists. Change it to :
this.setState({
index: (this.state.index +1) % this.props.images.length
});
And if will work.
Here is your Bin.

Categories

Resources