I am getting buffered image/jpeg from backend. Parent component does setState every time a new image is received and is passed down to Child component.
Child Component receives every update and can be seen in console logs, but it is only displaying single image.
Parent component:
class App extends Component {
constructor(props) {
super(props);
this.state = {
nodeResponse: ''
}
}
getFileStatusFromNode = (data) => {
this.setState({nodeResponse: data})
}
render() {
let CardView = nodeResponse !== '' &&
<Card key={nodeResponse.fileName} name={nodeResponse.fileName} image={nodeResponse.buffer} />
return (
<div className="App tc">
{ CardView }
</div>
)
}
}
Child component:
class Card extends PureComponent {
constructor({props}) {
super(props);
this.state = {
src: '',
};
}
componentDidMount() {
console.log("Card mounted")
this.setState(prevState => ({
src: [this.props.image, ...prevState.src]
}), () => console.log(this.state.src, this.props.name));
}
render() {
const { name } = this.props;
const { src } = this.state;
return (
<a style={{width: 200, height: 250}} key={name} className={'tc'} >
<div id='images'>
<img style={{width: 175, height: 175}} className='tc' alt='missing' src={`data:image/jpeg;base64, ${src}`}/>
</div>
</a>
)
}
}
NOTE: These images are coming from socket io. I want to display them in real time rather than creating a list first and then display together.
You are only rendering 1 <Card> when defining the <CardView> component.
Assuming nodeResponse.imageFiles is an array of files, you should have something like the following:
class App extends Component {
constructor(props) {
super(props);
this.state = {
nodeResponse: ''
}
}
getFileStatusFromNode = (data) => {
this.setState({nodeResponse: data})
}
render() {
let CardView
if(this.state.nodeResponse !== '') {
CardView = this.state.nodeResponse.imageFiles.map(file => (
<Card
key={image.fileName}
name={image.fileName}
image={image.buffer} />
)
)}
return (
<div className="App tc">
{ CardView }
</div>
)
}
}
Try with
class App extends Component {
constructor(props) {
super(props);
this.state = {
nodeResponse: []
}
}
getFileStatusFromNode = (data) => {
// here you merge the previous data with the new
const nodeResponse = [...this.state.nodeResponse, data];
this.setState({ nodeResponse: nodeResponse });
}
render() {
return (<div className="App tc">
{this.state.nodeResponse.map(n => <Card key={n.fileName} name={n.fileName} image={n.buffer} />)}
</div>);
}
}
and in your child component
class Card extends PureComponent {
render() {
const { src } = this.props;
return (<a style={{width: 200, height: 250}} className="tc">
<div id='images'>
<img style={{width: 175, height: 175}} className='tc' alt='missing' src={`data:image/jpeg;base64, ${src}`}/>
</div>
</a>);
}
}
Related
so I was working on a basic Todo app using React.js and I was wondering why the todo component does not automatically re-render once the state changed (the state contains the list of todos- so adding a new todo would update this array)? It is supposed to re-render the Header and the Todo component of the page with the updated array of todos passed in as props. Here is my code:
import React from 'react';
import './App.css';
class Header extends React.Component {
render() {
let numTodos = this.props.todos.length;
return <h1>{`You have ${numTodos} todos`}</h1>
}
}
class Todos extends React.Component {
render() {
return (
<ul>
{
this.props.todos.map((todo, index) => {
return (<Todo index={index} todo={todo} />)
})
}
</ul>
)
}
}
class Todo extends React.Component {
render() {
return <li key={this.props.index}>{this.props.todo}</li>
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.addnewTodo = this.addnewTodo.bind(this);
}
addnewTodo = () => {
let inputBox = document.getElementById("input-box");
if (inputBox.value === '') {
return;
}
this.props.handleAdd(inputBox.value);
}
render() {
return (
<div>
<input id="input-box" type="text"></input>
<button type="submit" onClick={this.addnewTodo}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { todos: ['task 1', 'task 2', 'task 3']}
this.handleNewTodo = this.handleNewTodo.bind(this);
}
handleNewTodo(todo) {
let tempList = this.state.todos;
tempList.push(todo);
this.setState = { todos: tempList };
}
render() {
return (
<div>
<Header todos={this.state.todos} />
<Todos todos={this.state.todos} />
<Form todos={this.state.todos} handleAdd={this.handleNewTodo} />
</div>
)
}
}
You are not updating the state correctly.
You need to make a copy of the this.state.todos, add the new todo in the copied array and then call this.setState function
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
Notice that this.setState is a function
You're updating state incorrectly,
handleNewTodo(todo) {
let tempList = [...this.state.todos];
tempList.push(todo);
this.setState({ todos: tempList });
}
This is the correct syntax.
I have 3 components. App.js - Main. localLog.jsx stateless, LoadBoard.jsx statefull. I want to Take string of data from LoadBoard and display it in localLog.jsx. The problem is that I can't figure it out why LocalLog is not displaying on screen.
console.log(this.data.Array) in App.jsx localLog is ["configuration"]
(2) ["configuration", "It's good configuration"]
App.jsx
class App extends Component {
constructor(props) {
super(props);
this.dataArray = [];
this.state = {
headers: []
};
this.localLog = this.localLog.bind(this);
}
localLog(data) {
if (data) {
this.dataArray.push(data);
console.log(this.dataArray);
this.dataArray.map(data => {
return <LocalLog info={data} />;
});
}
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.localLog} />
<pre id="log_box">{this.localLog()}</pre>
</>
);
}
}
localLog.jsx
let localLog = props => {
return (
<pre className={classes.background}>
<ul className={classes.ul}>
<li>{props.info}</li>
<li>hello world</li>
</ul>
</pre>
);
};
export default localLog;
LoadBoard.jsx
class LoadBoard extends Component {
constructor(props) {
super(props);
this.state = {
positionToId: []
};
}
componentDidMount() {
this.props.localLog("configuration");
this.props.localLog(`It's good configuration`);
}
render() {
return (
<div>
<h1>Nothing interesting</h1>
</div>
);
}
}
You are not returning anything from the localLog method, should be:
return this.dataArray.map(data => {
return <LocalLog info={data} />;
});
EDIT:
here is what your App component should look like.
class App extends Component {
constructor(props) {
super(props);
this.state = {
headers: [],
logs: []
};
this.addLog = this.addLog.bind(this);
}
// Add log to state
addLog(log) {
this.setState(state => ({
...state,
logs: [...state.logs, log]
}));
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.addLog} />
<pre id="log_box">
{this.state.logs.map(log => {
return <LocalLog info={log} />;
})}
</pre>
</>
);
}
}
you should use setState method in order to re-render the component.
you can try this.
class App extends Component {
constructor(props) {
super(props);
this.state = {
headers: [],
dataArray: []
};
this.localLog = this.localLog.bind(this);
}
localLog(data) {
if (data) {
this.state.dataArray.push(data);
this.setState({dataArray: this.state.dataArray})
}
}
render() {
return (
<>
<LoadBoard apiBase={this.state.apiBase} localLog={this.localLog} />
<pre id="log_box">{this.state.dataArray.map(i => <LoaclLog info={i}/>)}</pre>
</>
);
}
}
i have a problem - i need to change parent component's state from child component. I tried standard variant with props, but it's not helping in my case.
Here is code of Parent component:
class ThemesBlock extends React.Component {
constructor(props){
super(props);
this.state = {
currentThemeId: 0
}
}
changeState(n){
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
data.map(function (item) {
return <Theme key={item.id} data={item} changeState=
{item.changeState}/>
})
)
}
}
And here is my code for Child Component:
class Theme extends React.Component {
constructor(props){
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const { id, themename } = this.props.data;
const link = '#themespeakers' + id;
return (
<li><a href={link} onClick={() => this.changeState(id)}
className="theme">{themename}</a></li>
)
}
}
The primary issue is that changeState should be bound to the ThemesBlock instance, not to the Theme instance (by ThemesBlock).
Here's an example where I've bound it in the ThemesBlock constructor (I've also updated it to show what theme ID is selected):
class ThemesBlock extends React.Component {
constructor(props) {
super(props);
this.state = {
currentThemeId: 0
}
this.changeState = this.changeState.bind(this);
}
changeState(n) {
this.setState({currentThemeId: n})
}
render() {
let { data } = this.props;
return (
<div>
<div>
Current theme ID: {this.state.currentThemeId}
</div>
{data.map(item => {
return <Theme key={item.id} data={item} changeState={this.changeState} />
})}
</div>
)
}
}
class Theme extends React.Component {
constructor(props) {
super(props);
this.changeState = this.props.changeState.bind(this);
}
render() {
const {
data: {
id,
themename
},
changeState
} = this.props;
const link = '#themespeakers' + id;
return (
<li>{themename}</li>
)
}
}
const data = [
{id: 1, themename: "One"},
{id: 2, themename: "Two"},
{id: 3, themename: "Three"}
];
ReactDOM.render(
<ThemesBlock data={data} />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
Did you try to move map() before return? Like this:
render() {
let { data } = this.props;
const outputTheme = data.map(function (item) {
return <Theme key={item.id} data={item} changeState= {item.changeState}/>
})
return (
{outputTheme}
)
}
I am a React novice, so this might seem really simple, or maybe it isn't, I'm not sure. I'm building a basic to-do list. I want a mouseover effect on list items to pop up "delete this" text. But for my code so far, when I mouseover a list item, "delete this" pops up for all list items instead of just the one.
When I tried solving this by creating a new component for individual list items, that didn't seem to work. Any help is much appreciated!
class ToDosContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
heading: 'Something You Need To Do?',
todos: [
'wake up',
'brush your teeth'
],
}
this.addToDo = this.addToDo.bind(this)
}
addToDo(todo) {
this.setState((state) => ({
todos: state.todos.concat([todo])
}))
}
render() {
return (
<div>
<h1>To Do List</h1>
<h3>{this.state.heading}</h3>
<AddToDo addNew={this.addToDo} />
<ShowList tasks={this.state.todos} />
</div>
)
}
}
class AddToDo extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
this.updateNewToDo = this.updateNewToDo.bind(this)
this.handleAddToDo = this.handleAddToDo.bind(this)
}
//I think I can delete this part
updateNewToDo(e) {
this.setState({
newToDo: e.target.value
})
}
//
handleAddToDo() {
this.props.addNew(this.state.newToDo)
this.setState({
newToDo: ''
})
}
render() {
return (
<div>
<input
type="text"
value={this.state.newToDo}
onChange={this.updateNewToDo}
/>
<button onClick={this.handleAddToDo}> Add To Do </button>
</div>
)
}
}
class ShowList extends React.Component {
constructor(props) {
super(props)
this.state = {
newToDo: ''
}
}
onMouseOver(e) {
this.setState({
text: 'delete me'
})
console.log('hey')
}
onMouseOut(e) {
this.setState({
text: ''
})
console.log('hey hey')
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map((todo) => {
return <li onMouseEnter={this.onMouseOver.bind(this)} onMouseLeave={this.onMouseOut.bind(this)}> {todo} {text}</li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<ToDosContainer />, document.getElementById('helloworld'));
I would make a Task component. You don't want to set the state of the text in the ListView component, because then this.state.text is shared by each task in the map. Each task should be aware of its own hover, and not the hover of the other children.
class Task extends React.Component {
state = {
text: ""
};
onMouseOver(e) {
this.setState({
text: "delete me"
});
}
onMouseOut(e) {
this.setState({
text: ""
});
}
render() {
return (
<li
onMouseEnter={this.onMouseOver.bind(this)}
onMouseLeave={this.onMouseOut.bind(this)}
>
{this.props.todo} {this.state.text}
</li>
);
}
}
class ShowList extends React.Component {
constructor(props) {
super(props);
this.state = {
newToDo: ""
};
}
render() {
const { text } = this.state;
return (
<div>
<h4>To Do's</h4>
<ul>
{this.props.tasks.map(todo => {
return <Task todo={todo} />;
})}
</ul>
</div>
);
}
}
I'm trying to build a little React app that when a user selects a meat product the ui will show that category of meat from the json data file. I have a Products class as the parent to a class ProductCategoryRow and a SelectProduct function. I'm a little confused as to how to set the state in the Products class and pass the props on to ProductCategoryRow and SelectProduct.
function SelectProduct (props) {
const products = ['All', 'Beef', 'Lamb', 'Poultry'];
return (
<ul className='products'>
{products.map(function (prod) {
return (
<li
style={prod === props.selectedProduct ? { color: '#d0021b' }:null}
onClick={props.onSelect.bind(null, prod)}
key={prod}>
{prod}
</li>
)
})}
</ul>
)};
class ProductCategoryRow extends React.Component {
constructor(props) {
super(props);
this.state = {
list: list,
};
this.onDismiss = this.onDismiss.bind(this);
}
onDismiss(id) {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
return (
<div className="container">
{ this.state.list.map(item =>
<div key={item.objectID} className="product-category-row">
<div>{item.category}</div>
<div>{item.meatCut}</div>
<div>{item.cooking}</div>
<div>{item.price}</div>
<div>
<button
onClick={() => this.onDismiss(item.objectID)}
type="button"
>
Dismiss
</button>
</div>
</div>
)}
</div>
)
}
}
SelectProduct.propTypes = {
selectedProduct: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
}
class Products extends React.Component {
constructor (props) {
super(props);
this.state = {
selectedProduct: 'All',
lists: null
};
this.updateProduct = this.updateProduct.bind(this);
}
componentDidMount () {
this.updateProduct(this.state.selectedProduct);
}
updateProduct(prod) {
this.setState(function () {
return {
selectedProduct: prod,
lists: null
}
})
}
render() {
return (
<div>
<SelectProduct
selectedProduct={this.state.selectedProduct}
onSelect={this.updateProduct}
/>
<ProductCategoryRow />
</div>
)
}
}
export default Products;
Thanks in advance
You are already updating your parent component state when a product is selected, so that is good! Now, you need to pass in this.state.selectedProduct to the ProductCategoryRow component as a selectedProduct prop in your render method:
function SelectProduct (props) {
const products = ['All', 'Beef', 'Lamb', 'Poultry'];
return (
<ul className='products'>
{products.map(function (prod) {
return (
<li
style={prod === props.selectedProduct ? { color: '#d0021b' }:null}
onClick={props.onSelect.bind(null, prod)}
key={prod}>
{prod}
</li>
)
})}
</ul>
)};
class ProductCategoryRow extends React.Component {
constructor(props) {
super(props);
this.state = {
list: list,
};
this.onDismiss = this.onDismiss.bind(this);
}
onDismiss(id) {
const isNotId = item => item.objectID !== id;
const updatedList = this.state.list.filter(isNotId);
this.setState({ list: updatedList });
}
render() {
return (
<div className="container">
{ this.state.list.map(item =>
<div key={item.objectID} className="product-category-row">
<div>{item.category}</div>
<div>{item.meatCut}</div>
<div>{item.cooking}</div>
<div>{item.price}</div>
<div>
<button
onClick={() => this.onDismiss(item.objectID)}
type="button"
>
Dismiss
</button>
</div>
</div>
)}
</div>
)
}
}
SelectProduct.propTypes = {
selectedProduct: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
}
class Products extends React.Component {
constructor (props) {
super(props);
this.state = {
selectedProduct: 'All',
lists: null
};
this.updateProduct = this.updateProduct.bind(this);
}
componentDidMount () {
this.updateProduct(this.state.selectedProduct);
}
updateProduct(prod) {
this.setState(function () {
return {
selectedProduct: prod,
lists: null
}
})
}
render() {
return (
<div>
<SelectProduct
selectedProduct={this.state.selectedProduct}
onSelect={this.updateProduct}
/>
<ProductCategoryRow selectedProduct={this.state.selectedProduct} />
</div>
)
}
}
export default Products;