I'm developing an App in ReactJS, and I have a page where I want to show two select, one dependent on the other.
I'm using react-select and #material-ui.
In dates:
[
{
"id": 1,
"name": "202001"
},
{
"id": 2,
"name": "202002"
},
{
"id": 3,
"name": "202003"
},
{
"id": 4,
"name": "202004"
},
{
"id": 5,
"name": "202005"
},
{
"id": 6,
"name": "202006"
},
{
"id": 7,
"name": "202007"
}
]
I have a list of dates that are available to select.
import React, { useState, useEffect } from "react";
import Select from "react-select";
...
const App = () => {
...
const DateA = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const DateB = dates.map((item) => ({
value: item.id,
label: item.name,
}));
const [dateA, setDateA] = React.useState(null);
const [dateB, setDateB] = React.useState(null);
function handleChangeDateA(value) {
setDateA(value);
}
function handleChangeDateB(value) {
setDateB(value);
}
return (
<div className="App">
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateA"
TextFieldProps={{
label: "DateA",
InputLabelProps: {
htmlFor: "DateA",
shrink: true,
},
placeholder: "DateA...",
}}
options={DateA}
components={components}
value={dateA}
onChange={handleChangeDateA}
/>
</div>
<div className="col-3">
<Select
classes={classes}
styles={selectStyles}
inputId="DateB"
TextFieldProps={{
label: "DateB",
InputLabelProps: {
htmlFor: "DateB",
shrink: true,
},
placeholder: "DateB...",
}}
options={DateB}
components={components}
value={dateB}
onChange={handleChangeDateB}
/>
</div>
</div>
);
};
export default App;
The idea is that the DateB select take dates greater than the ones selected in the DateA select.
How can I do this, suggestions?
Try this
useEffect(() => {
if (condition) {
setDateB(value)
}
}, [DateA])
Related
Today i want to create something like form in react native that looks like
It's pretty simple. i used this lib for radio button. However i want to change this text in the button when i click button next. I used following code.
import React, { useState, useRef } from "react";
import { StyleSheet, View, Button } from "react-native";
import RadioButtonRN from "radio-buttons-react-native";
export default function App() {
const numRef = useRef(0);
const questions = [
{
question: "What is localhost's IP address?",
answers: [
{ id: "1", text: "192.168.1.1" },
{ id: "2", text: "127.0.0.1", correct: true },
{ id: "3", text: "209.85.231.104" },
{ id: "4", text: "66.220.149.25" },
],
},
{
question: "What kind of11 fruit was used to name a computer in 1984?",
answers: [
{ id: "1", text: "Blackberry" },
{ id: "2", text: "Blueberry" },
{ id: "3", text: "Pear" },
{ id: "4", text: "Apple", correct: true },
],
},
];
return (
<View>
<RadioButtonRN
data={questions[numRef.current].answers.map((item) => ({
label: item.text,
correct: item.correct,
}))}
selectedBtn={(e) => {
console.log(e);
}}
/>
<Button
title="Next"
onPress={() => {
numRef.current = numRef.current + 1;
}}
/>
</View>
);
}
So right now when i clicked on the next button, the only thing thats updated is variable
numRef
But questions[numRef.current] doesn't update text in the button.
How can i fix that?
Changing the value of a ref doesn't result in a re-render. For data that, when it gets changed, should result in a re-render, you should use state instead.
export default function App() {
const [num, setNum] = useState(0);
// ...
data={questions[num].answers.map((item) => ({
// ...
onPress={() => {
setNum(num + 1)l
}}
I have used material-table with react to render my data. I want to show the hover effect and cursor pointer effect when I hover on rows. But I couldn't find this even in the documentation.
IN SHORT- I want to highlight some colors on a row when the cursor hovers to that row.
Note: I also found a similar question and answer here, but they used another state just to hover which downgrades the performance if I increase the data like thousands rows. Hence it's bad practice so here I am asking for alternate solutions for the same.
Here is my codesandbox link
Below I also pasted my code.
App.js
import React, { useState } from 'react';
import './App.css';
import MaterialTable from 'material-table'
const empList = [
{ id: 1, name: "Neeraj", email: 'neeraj#gmail.com', phone: 9876543210, city: "Bangalore" },
{ id: 2, name: "Raj", email: 'raj#gmail.com', phone: 9812345678, city: "Chennai" },
{ id: 3, name: "David", email: 'david342#gmail.com', phone: 7896536289, city: "Jaipur" },
{ id: 4, name: "Vikas", email: 'vikas75#gmail.com', phone: 9087654321, city: "Hyderabad" },
]
function App() {
const [data, setData] = useState(empList)
const columns = [
{ title: "ID", field: "id", editable: false },
{ title: "Name", field: "name" },
{ title: "Email", field: "email" },
{ title: "Phone Number", field: 'phone', },
{ title: "City", field: "city", }
]
return (
<div className="App">
<h1 align="center">React-App</h1>
<h4 align='center'>Material Table with CRUD operation</h4>
<MaterialTable
title="Employee Data"
data={data}
columns={columns}
editable={{
onRowAdd: (newRow) => new Promise((resolve, reject) => {
const updatedRows = [...data, { id: Math.floor(Math.random() * 100), ...newRow }]
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowDelete: selectedRow => new Promise((resolve, reject) => {
const index = selectedRow.tableData.id;
const updatedRows = [...data]
updatedRows.splice(index, 1)
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
}),
onRowUpdate:(updatedRow,oldRow)=>new Promise((resolve,reject)=>{
const index=oldRow.tableData.id;
const updatedRows=[...data]
updatedRows[index]=updatedRow
setTimeout(() => {
setData(updatedRows)
resolve()
}, 2000)
})
}}
options={{
actionsColumnIndex: -1, addRowPosition: "first"
}}
/>
</div>
);
}
export default App;
add in your inside of your CSS
tbody tr:hover {
background-color: blue;
color: white;
cursor: pointer;
}
This question already has answers here:
How to sort array of objects based on a boolean property?
(2 answers)
Javascript sort array of objects by a boolean property
(14 answers)
Closed 3 years ago.
I have a JSON file:
[
{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
}
]
What I would like to achieve is for the JSON with "availability : true" to automatically appear above the "availability : false". So that the pink appears above the blue like this:
This is my code so far which simply displays them in the same order as the JSON file:
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
posts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="color">{post.color}</p>
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
I am unsure of how to achieve this and have struggled to find any suggestions so far so any help would be great.
var data = [{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
},
{
"id": 3,
"color": "Pink",
"availability": true
},
{
"id": 4,
"color": "Pink",
"availability": false
}, {
"id": 5,
"color": "Pink",
"availability": true
}
]
//unordered
data.sort(val => val.availability ? -1 : 1) //simple one line sort function for boolean
console.log(data);
// ordered based on your default array order
data = [{
"id": 1,
"color": "Blue",
"availability": false
},
{
"id": 2,
"color": "Pink",
"availability": true
},
{
"id": 3,
"color": "Pink",
"availability": true
},
{
"id": 4,
"color": "Pink",
"availability": false
}, {
"id": 5,
"color": "Pink",
"availability": true
}
]
data.sort((a, b) => b.availability - a.availability);
console.log(data);
You can filter the two sets, create a new array and then render it.
import React, { Component } from 'react';
import './styles.css'
class GetOnlinePosts extends Component {
constructor(props){
super(props);
this.state = {
error : null,
isLoaded : false,
posts : []
};
}
componentDidMount(){
fetch("https://api.myjson.com")
.then( response => response.json())
.then(
(result) => {
this.setState({
isLoaded : true,
posts : result
});
},
(error) => {
this.setState({
isLoaded: true,
error
})
},
)
}
render() {
const {error, isLoaded, posts} = this.state;
const orderedPosts = [...posts.filter((post) => post.availability), ...posts.filter((post) => !post.availability)]
if(error){
return <div>Error in loading</div>
}else if (!isLoaded) {
return <div>Loading ...</div>
}else{
return(
<div>
<div className="tiles">
{
orderedPosts.map(post => (
<div key={post.id}>
<div className="tile">
<p className="color">{post.color}</p>
</div>
</div>
))
}
</div>
</div>
);
}
}
}
export default GetOnlinePosts;
I am making a guide me section. What this guide me section does - displays an array of processes. Within each process is an array of steps, within each step is an array of options. The user selects an option from one of the steps, it takes them to the next correlating step. If the user selects the option on step 2, it could take them to step 3 or back to step 1. It depends on the id.
With all that said I'm having issues with my Process mutating on me. I'm using React Context as a global state. When a user selects an option, I'm grabbing that id, then filtering the designated object by that id. So I should only be left is that processes with that specific step. What's happening is my initial global state is mutating somehow. I'm missing something here as I'm new to React.
P.s - I'm not using any services at this moment, so I just copied some JSON over to my initial state in context.js
context.js
import React, { Component } from 'react'
// import axios from 'axios'
const Context = React.createContext()
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_PROCESS':
return {
...state,
guides: action.payload
}
default:
return state
}
}
export class Provider extends Component {
state = {
guides: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585x",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
initialGuide: [
{
"processName": "Support Messaging",
"steps": [{
"id": "15869594739302",
"title": "step one",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 1 text",
"type": "option"
},
{
"nextStep": "4767fn-47587n-2819am-9585j,04956840987",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
},
{
"id": "04956840987",
"title": "step two",
"options": [{
"nextStep": "4767fn-47587n-2819am-9585j,15869594739302",
"text": "Return to step1",
"type": "option"
},
{
"nextStep": "",
"text": "Option 2 text",
"type": "option"
},
{
"nextStep": "",
"text": "Option 3 text",
"type": "option"
}
]
}
],
"owner": "bob",
"id": "4767fn-47587n-2819am-9585j",
"lastUpdated": 154222227099000,
"tags": ["Tag1", "Tag2", "Tag3"]
}
],
dispatch: action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer;
Guides.js
import React, { Component } from 'react'
import { Consumer } from '../../context'
import Process from './Process'
class Guides extends Component {
constructor (props) {
super(props)
this.state = {
contextValue: [],
searchData: props.location.data
}
}
render () {
console.log(this.props.location.data, this.state, 'logging state and props on guides')
// this.state.searchData = this.props.location.data
return (
<Consumer>
{value => {
return (
<React.Fragment>
<div className="content-wrapper">
<h1>Guide Me</h1>
<div className="ms-Grid times--max-width" dir="ltr">
<div className="ms-Grid-row">
<div className="profile--wrapper ms-Grid-col ms-sm12 ms-md12 ms-lg5">
{value.guides.map(item => {
return <Guide key={item.id} guide={item} processValue={value.guides} initialGuide={value.initialGuide}/>
})}
</div>
</div>
</div>
</div>
</React.Fragment>
)
}}
</Consumer>
)
}
}
export default Guides
Process.js
import React, { Component } from 'react'
import GuideSteps from './Guide-Steps'
import { Consumer } from '../../context'
class Process extends Component {
constructor(props) {
super(props)
this.state = {
processName: this.props.guide.processName,
process: this.props.guide,
steps: this.props.guide.steps,
selectedIndex: 0,
selectedStep: '',
processValue: this.props.processValue,
initialGuide: this.props.initialGuide
}
this.displayStep = this.displayStep.bind(this)
}
displayStep = (res, dispatch) => {
this.setState({ selectedStep: res })
}
render() {
const { steps, selectedIndex, process, processName, processValue, initialGuide } = this.state
return (
<Consumer>
{value => {
return (
<div>
<h2 className="profile--sub-header--bold">{processName}</h2>
<GuideSteps
key={this.props.guide.steps[selectedIndex].id}
selectedStep={this.props.guide.steps[selectedIndex]}
stepValue={this.displayStep}
process={process}
processValue={processValue}
initialGuide={initialGuide}
/>
</div>
)
}}
</Consumer>
)
}
}
export default Process
Guide-Steps.js
import React, { Component } from 'react'
import { ChoiceGroup } from 'office-ui-fabric-react/lib/ChoiceGroup'
import { Consumer } from '../../context'
class GuideSteps extends Component {
constructor(props) {
super(props);
this.state = {
process: [],
selectedStep: this.props.selectedStep,
dispatch: '',
processValue: this.props.processValue,
initialGuide: ''
}
this._onChange = this._onChange.bind(this)
}
_onChange = (ev, option) => {
// this.props.stepValue(option.value.nextStep)
const { dispatch , initialGuide } = this.state
let optionArray = option.value.nextStep.split(',')
let processArray = this.state.process.filter(item => {
return item.id === optionArray[0]
})
let stepArray = processArray[0].steps.filter(item => {
return item.id === optionArray[1]
})
console.log(stepArray, processArray, this.state.process, 'logging step array before setting')
processArray[0].steps = stepArray
console.log(stepArray, processArray, this.state.process, 'logging step array after setting')
dispatch({
type: 'SEARCH_PROCESS',
payload: processArray
})
}
render() {
let options = []
{
this.props.selectedStep.options.map(item => {
return options.push({
key: item.text,
text: item.text,
value: item
})
})
}
return (
<Consumer>
{value => {
const { dispatch, guides, initialGuide } = value
this.state.dispatch = dispatch
console.log(value, 'logging initial guide in render')
this.state.process = initialGuide
return (
<div>
<ChoiceGroup
className="defaultChoiceGroup"
options={options}
onChange={this._onChange}
/>
</div>
)
}}
</Consumer>
)
}
}
export default GuideSteps
On change in GuideSteps is where I'm doing the logic for filtering and setting up my new object.
EDIT
This fixed the issue but I think it's too expensive. How would I go about solving this issue without having to reparse the array.
update: (ev, option) => {
const { initialGuide } = this.state
if (option.value.nextStep !== null && option.value.nextStep !== '') {
//split string
const optionArray = option.value.nextStep.split(',')
//filter process array
const processArray = initialGuide.filter(process => {
return process.id === optionArray[0]
})
//filter step array
const stepArray = processArray[0].steps.filter(
item => item.id === optionArray[1]
)
if(stepArray.length > 0 && stepArray !== null) {
//get a copy of the process array so original is not mutated by the steps
let stringC = JSON.stringify(processArray)
let stringD = JSON.parse(stringC)
stringD[0].steps = stepArray
//issue might be here visually where setting the state happens quickly, therefore radio button visual does not display in time.
setTimeout(() => {
this.setState({ guides: stringD })
}, 200)
}
}
},
this.state.process = initialGuide
let processArray = this.state.process.filter...
processArray[0].steps = stepArray
So it looks like you're mutating initialGuide via reference.
I'm kind of lost to access some info in my static data. Here's the data :
{
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
}
Then I get the first info like that :
this.setState({
currentLabel: this.state.labels["info1"]
})
This is why I want and then I want to display info in a component and it's working until I try to get tags information. I tried a .map() but without success and error.
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
<Text>How do I get "tags" information</Text>
</View>
Is it possible to access these objects in the array "tags" ?
yes you can call tags as follows infoDetail.tags and do map on it
render(){
const tagItems = infoDetail && infoDetail.tags.map((item, index) => {
return <Text key={index}>{item.name}</Text>
});
return(
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{tagItems}
</View>
)
}
Here is a full working code. Since your labels state property is an object, you need to map it somehow. I've chosen Object.values here. You can use Object.keys or even Object.entries according to your needs.
I've used a separate Info component and passed the values to it, then render there. In this component, we are again mapping the tags, then rendering the list.
class App extends React.Component {
state = {
labels: {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter",
},
{
name: "#myspace",
},
],
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram",
},
{
name: "#facebook",
},
],
},
},
}
render() {
const { labels } = this.state;
return (
<div>
{
Object.values( labels ).map( value =>
<Info label={value} key={value.label} /> )
}
</div>
);
}
}
const Info = ( props ) => {
const { title, text, tags } = props.label;
const tagList = tags.map( tag => <p key={tag.name}>{tag.name}</p> );
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Update
If your data is totally static then #Xavi A.'s method is a good option. I don't know how is your list but I provide a simple code including something like you want here.
const labels = {
info1: {
label: "label1",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#twitter"
},
{
name: "#myspace"
}
]
},
info2: {
label: "label2",
class: "class-css",
title: "title",
text: "text",
number: "20",
tags: [
{
name: "#instagram"
},
{
name: "#facebook"
}
]
}
};
class App extends React.Component {
state = {
currentLabel: Object.keys(labels)[0]
};
handleInfoChange = info => this.setState({ currentLabel: info });
renderList = () => (
<ul>
{Object.keys(labels).map(info => (
<Item key={info} info={info} onClick={this.handleInfoChange} />
))}
</ul>
);
render() {
const { currentLabel } = this.state;
return (
<div>
{this.renderList()}
<Info currentLabel={currentLabel} />
</div>
);
}
}
const Item = props => {
const { info, onClick } = props;
const handleClick = () => onClick(info);
return <li onClick={handleClick}>{info}</li>;
};
const Info = props => {
const { currentLabel } = props;
const { title, text, tags } = labels[currentLabel];
const tagList = tags.map(tag => <p key={tag.name}>{tag.name}</p>);
return (
<div style={{ border: "1px solid gray", marginTop: "-1px" }}>
<p>{title}</p>
<p>{text}</p>
<div>{tagList}</div>
</div>
);
};
ReactDOM.render( <App />, document.getElementById( "root" ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Probably something like this.
<Text>{infoDetail.tags.map(tag => {/*render */})}</Text>
You can try Object.keys() and Array.prototype.reduce() to get your favorite data:
const data = {
"info1": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#twitter"
},
{
"name": "#myspace"
}
]
},
"info2": {
"label": "label",
"class": "class-css",
"title": "title",
"text": "text",
"number": "20",
"tags": [
{
"name": "#instagram"
},
{
"name": "#facebook"
}
]
}
};
const tags = Object.keys(data).reduce((result, key) => {
return result.concat(data[key].tags);
}, [])
console.log(tags);
/* tags = [
{
"name": "#twitter"
},
{
"name": "#myspace"
},
{
"name": "#instagram"
},
{
"name": "#facebook"
}
] */
No need to save all the static data in your state, you can keep your state cleaner by just saving the selected label:
onLabelSelect = label => {
//label will be "info1" for example
this.setState({
currentLabel: label
})
}
Then in your render:
render(){
//get infoDetail from staticData
const infoDetail = staticData[this.state.currentLabel]
return (
<View>
<Text>{infoDetail.title}</Text>
<Text>{infoDetail.text}</Text>
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
</View>
)
}
Note about the map. This:
{infoDetail.tags.map( ({name}) => <Text>name</Text>)}
is a shorter version of:
{infoDetail.tags.map( item => {
return <Text>item.name</Text>
})}