ReactJS: Rendering the state separately and not as an array - javascript

I ended up pulling off what I wanted. However, it's giving me an array of the state instead of rendering each one separately. This is probably very simple and I'm more than likely over-complicating it but hey, any help would be nice.
Here's what I currently am dealing with
And here's a better example: https://i.imgur.com/WLDkbOb.gif
And lastly here's probably the best overview: https://imgur.com/a/zintqTA
constructor(props) {
super(props);
this.state = {
data: [],
loading: false,
}
}
ws = new WebSocket(URL)
componentDidMount() {
this.ws.onopen = () => {
console.log('connected')
}
this.ws.onmessage = e => {
const tbox = JSON.parse(e.data);
if(tbox.data && tbox.data.length > 0){
this.setState({
data : this.state.data.concat(tbox.data[0]),
})
}
}
this.ws.onclose = () => {
console.log('disconnected')
this.setState({
ws: new WebSocket(URL),
})
}
}
render() {
let { data } = this.state;
const chatBox = data.map(item => {
return (
<List
key={item.id}
dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user} {item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>
)
})
return (
<div>
<div>
{chatBox}
</div>
I'm trying to loop through the state and render each message separately

I think you don't need to loop through this.state.data[] because you are already setting data source to antd <List> component. antd List component handles collection of objects for us.
This would be the code for rendring your this.state.data:
const chatBox = <List dataSource={this.state.data}
renderItem={item => (
<List.Item >
<List.Item.Meta
avatar={<Avatar size="large" icon="user" />}
title={<div>{item.user}
{item.date}</div>}
description={item.message}
/>
</List.Item>
)}
>
</List>;
you can have a look at these links :
https://stackblitz.com/run
https://ant.design/components/list/

Related

Change switch value without change all of them

I'm a beginner and I don't know how to change switch value without change all of them. You can see the code about. I'm mapping a nested array on return. I'm confuse how to change the switch status individually. I've tried some solutions like change the value inside the array with an onchange event but it doenst reender the screen.
const [dtData, setDtData] = useState('release_data');
useFocusEffect(
React.useCallback(() => {
getManagerListDay(function(resultado) {
setDtData(resultado);
});
setInterval(() => {
getManagerListDay(function(resultado) {
setDtData(resultado);
});
toasted.showToast('Refresh');
}, 60000);
}, [])
);
const section = [];
for ( var i = 0, ii = dtData.length; i < ii; i++ ) {
if(i>0?dtData[i].dayDate != dtData[i-1].dayDate:true == true){
const items = [];
for ( var b = 0, bb = dtData.length; b < bb; b++ ) {
if(dtData[b].dayDate == dtData[i].dayDate){
items.push({
name: dtData[b].name,
worked: dtData[b].worked,
workedWeekDays: dtData[b].workedWeekDays,
KeyItemDriver: dtData[b].KeyItemDriver
});
}
}
section.push({
dayDate: dtData[i].dayDate,
driverAmount: dtData[i].driverAmountAccepted,
keySectionDay: dtData[i].keySectionDay,
data: items
});
}
}
return (
<Root>
<View style={{marginTop:15}}>
{section.map((obj, index) => {
return(
<List.Section
title={obj.dayDate}
titleStyle={{color:'black',fontWeight:'bold', fontSize: 16}}
style={{backgroundColor:'rgba(79,79,79,0.1)', marginTop:0, borderRadius:30, width:'90%',alignSelf:'center'}}
key={index}
id={obj.keySectionDay} >
<Button mode="contained" style={{borderRadius:70,width:40,backgroundColor:'#48D1CC', marginLeft:'80%'}}>
<Text style={{fontWeight:'bold', marginRight:50}}> {obj.driverAmount} </Text> </Button>
<FAB style={styles.fab} color='white' small={false} icon="database-export" onPress={() => console.log('Pressed')} />
<List.Accordion
title="DRIVERS"
key={index}
theme={{ colors: { primary: '#48D1CC' }}}
titleStyle={{color:'black', fontSize:14}}
left={props => <List.Icon {...props} icon="folder"/>}>
{obj.data.map((dts,index) => {
return(
<List.Item key={index} id={dts.KeyItemDriver} title={dts.name} titleStyle={{color:'black'}}
style={{borderTopColor:'white', borderTopWidth:1}}
left={props => <Switch {...props} color={dts.worked=='S'?'#48D1CC':'red'} value={dts.worked!=null?true:false}/>}
right={props => <Text {...props} style={{color:'#48D1CC',fontSize:18, marginTop:5}} > {dts.workedWeekDays} </Text>}
onPress={() => alert('Sou eu')}
/>
)
})}
</List.Accordion>
</List.Section>
);
})
}
</View>
</Root>
);
Never mind! I was thinking wrong about the steps process. I need.
I just update the information on the database and rerender the screen from there. What I think is correct. I must show what is commited.
Thanks!

Invalid Hook Call - React Hooks

I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}

Render method called twice with componentDidMount - React native

I'm fetching some data from the firebase realtime database. I have created a state in the constructor and initialised as an empty array. Later in the componentDidUpdate method, I have updated the state with setState method. The issue is the render method called twice in the component and data is getting multiplied each time.
this.state = {
values: [],
}
componentDidMount = () => {
firebase.database().ref('Table').once('value', (data) => {
var input = data.val();
this.setState({ values: input })
})
}
And the render method:
var val = []; //global variable declared before class declaration
render() {
{
this.state.values.map(item => {
val.push(
<List>
<ListItem>
<Text>{item["value"]}</Text>
</ListItem>
</List>
)
})
}
return(
<View>
{val}
</View>
)
}
And the list item is keep getting multiplied each time when the component renders. I have checked the doc but couldn't get a proper solution.
https://reactjs.org/docs/react-component.html#componentdidmount
Where is val defined?
Okay. That I have defined a global var. Declared it as an array before the class declaration
That's where your duplication comes from.
Better do it this way:
render() {
const val = this.state.values.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
return <View>{val}</View>;
}
I didn't understand well the val variable but this code should work for you:
mapValues = list => list.map((item, index) => (
<List key={index}>
<ListItem>
<Text>{item.value}</Text>
</ListItem>
</List>
));
render() {
return (
<View>
{this.mapValues(this.state.values)}
</View>
);
}

onClick gets fired immediately on component mount using firestore and React, redux

I've got a component, that displays a project. For each project, there is a delete button, but for some reason, the delete buttons of all my projects get "pressed" immediately.
Why would this happen? I'm using redux and firestore. I think this has maybe something to do with the realtime listener, but don't have any problems with adding data to the database.
Projects.js
componentDidMount = () => {
this.props.projectActions.registerProjectListener();
};
renderProjects = () => {
const { projects } = this.props.projects;
const { classes, projectActions } = this.props;
return (
<Paper elevation={0} square={true} className={classes.projectPaper}>
<Box fontSize="h5.fontSize" textAlign="center">
Your Projects:
</Box>
{projects &&
projects.map(project => {
return <Project {...{ key: project.id, project, projectActions }}></Project>;
})}
<Grid container className={classes.grid} direction="row" justify="flex-end" alignItems="center">
<AddProject {...{ projectActions }}></AddProject>
</Grid>
</Paper>
);
};
Project.js
export class Project extends Component {
render() {
const { classes, project, projectActions } = this.props;
console.log(project.id);
return (
<Paper elevation={3} variant="outlined" className={classes.paper}>
<Grid container justify="space-between">
<Box fontSize="h6.fontSize">{project.name}</Box>
<IconButton onClick={projectActions.deleteProject(project.id)}> //This gets fired immediately for every project
<ClearIcon></ClearIcon>
</IconButton>
</Grid>
</Paper>
);
}
}
actions
export const deleteProject = id => {
return dispatch => {
console.log(db.collection("projects").doc(id));
// db.collection("projects")
// .doc(id)
// .delete();
dispatch({ type: ActionTypes.DELETE_PROJECT });
};
};
export const registerProjectListener = () => {
let projects = [];
return dispatch => {
db.collection("projects").onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === "added") {
console.log(change.doc.data());
projects.push({ ...change.doc.data(), ...{ id: change.doc.id } });
} else if (change.type === "removed") {
// projects.filter(()=> )
}
});
dispatch({ type: ActionTypes.REGISTER_LISTENER, projects: projects });
});
};
};
You need to pass reference to a function. Update the IconButton component with the following.
<IconButton
onClick={() => projectActions.deleteProject(project.id)}>
<ClearIcon></ClearIcon>
</IconButton>

Update list when redux store changes

I am trying to update the list when my redux store changes but for some odd reason it isn't. I have to manually refresh the page to see my changes. Here's the snippet of my List component and rowRenderer.
<InfiniteLoader
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={visibleRequest.length}
>
{({ onRowsRendered, registerChild }) => (
<AutoSizer>
{({ height, width }) => (
<List
ref={registerChild}
className="List"
height={height}
rowHeight={listRowHeight}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
width={width}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
_rowRenderer = ({ index, key, style }) => {
const { loadedRowsMap, selected } = this.state;
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
} else {
content = (
<div className="placeholder" style={{ width: _.random(100, 200) }} />
);
}
return (
<PendingChat
key={key}
content={content}
style={style}
row={row}
{...this.props}
/>
);
};
Yeah, I ran into the same problem. Its because the references to your objects don't change when you do
const row = this.getDatum(index);
let content;
if (loadedRowsMap[index] === STATUS_LOADED) {
content = row;
}
Take a look at immutability.

Categories