I am using react-select in a project. When a user clicks to update a post, I fetch the post's original data and fill the form. However I am not able to pre-fill the Select component.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
tags : [], // this contains the list of all the tags in system
incomingTags : [] // will contain the tags which are linked with the post
selectedTags : [] // contains the tags that have been selected by user
}
}
selectRef = null;
componentDidMount() {
fetch all the tags
this.setState({tags : fetchedTags});
fetch the post data
this.setState({incomingTags : fetchedPostTags});
}
handleTagInputChange = (selectedOptions) => {
this.setState({ selectedTags : selectedOptions });
}
render() {
return (
<CreatableSelect
ref={ref => {this.selectRef = ref;}}
isMulti
options={this.state.tags}
onChange={this.handleTagInputChange}
/>
)
}
}
Things I have tried -
Using defaultValue={this.state.defaultTags} but it does not change when the state is set.
Using value={this.state.defaultTags} but then I am not able to remove the selected tags or select any other tags. I went through a similar query - react-select: pre-select a value in multi-select dropdown
Using this.selectRef.selectOption() but it does not work for selecting multiple items
When you want to use controlled component, you have to set both value and onChange
import React, { Component } from 'react';
import CreatableSelect from 'react-select/creatable';
export default class CreatableSingle extends Component {
constructor(props) {
super(props);
this.state = {
tags : [],
}
}
handleChange = (
newValue
) => {
this.setState({ tags: newValue })
};
render() {
return (
<CreatableSelect
value={this.state.tags}
onChange={this.handleChange}
/>
);
}
}
Related
I'm creating a dropdown with multiple selection using check box,
I had used onClick in the MenuItem of the dropdown so user can click on the MenuItem which is parent of the checkbox to check and uncheck instead of clicking exactly on checkbox, This below code works and I can get the checked status of the check box when I render it with static value.
The same is not working I try to use dynamic values like using an array refer -FilterMultipleItems component, I'm getting undefined in the handleChange method for event.target.checked, I'm new to react, can anyone guide me, thanks.
class CustomButton extends React.Component {
render() {
const { onPress, isChecked, values} = this.props;
return (
<MenuItem onClick={onPress}>
<Checkbox
disabled
checked={isChecked}
value={values}/> {values}
</MenuItem>
);
}
}
class FilterMultipleItems extends React.Component {
state = {open: false,}
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange = name => event => {
console.log(name, event.target.checked)
//event.target.checked getting undefined
}
render() {
const { classes, value } = this.props;
const { open } = this.state;
// here I'm adding the dynamic values to the CustomButton Component
var filtersItems=[];
for(var i=0;i<this.props.value.filtersValues.length;i++){
var currentItem = this.props.value.filtersValues[i];
this.state[currentItem]= false;
filtersItems.push( <CustomButton onPress={this.handleChange(currentItem)} values={currentItem} checked={this.state[currentItem]} key={i}>Click on me</CustomButton>)
}
return (
<MenuList>
{/*rendering the component here*/}
{filtersItems}
</MenuList>
);
}
}
I have the following code that simply constructs blocks for our products and the selected state allows the component to be selected and unselected. How can I figure out which of these components are selected and limit the user to only selecting one at a time. This is ReactJS code
import React from 'react';
export default class singleTile extends React.Component{
constructor(props){
super(props);
this.title = this.props.title;
this.desc = this.props.desc;
this.svg = this.props.svg;
this.id = this.props.id;
this.state = {
selected: false
}
}
selectIndustry = (event) => {
console.log(event.currentTarget.id);
if(this.state.selected === false){
this.setState({
selected:true
})
}
else{
this.setState({
selected:false
})
}
}
render(){
return(
<div id={this.id} onClick={this.selectIndustry}className={this.state.selected ? 'activated': ''}>
<div className="icon-container" >
<div>
{/*?xml version="1.0" encoding="UTF-8"?*/}
{ this.props.svg }
</div>
</div>
<div className="text-container">
<h2>{this.title}</h2>
<span>{this.desc}</span>
</div>
</div>
)
}
}
You need to manage the state of the SingleTile components in the parent component. What i would do is pass two props to the SingleTile components. A onClick prop which accepts a function and a isSelected prop that accepts a boolean. Your parent component would look something like this.
IndustrySelector.js
import React from 'react';
const tileData = [{ id: 1, title: 'foo' }, { id: 2, title: 'bar' }];
class IndustrySelector extends Component {
constructor(props) {
super(props);
this.state = { selectedIndustry: null };
}
selectIndustry(id) {
this.setState({ selectedIndustry: id });
}
isIndustrySelected(id) {
return id === this.state.selectedIndustry;
}
render() {
return (
<div>
{tileData.map((data, key) => (
<SingleTile
key={key}
{...data}
onClick={() => this.selectIndustry(data.id)}
isSelected={this.isIndustrySelected(data.id)}
/>
))}
</div>
);
}
}
The way this works is as follows.
1. Triggering the onClick handler
When a user clicks on an element in SingleTile which triggers the function from the onClick prop, this.selectIndustry in the parent component will be called with the id from the SingleTile component.
Please note that in this example, the id is remembered through a
closure. You could also pass the id as an argument to the function of
the onClick prop.
2. Setting the state in the parent component
When this.selectIndustry is called it changes the selectedIndustry key of the parent component state.
3. Updating the isSelected values form the SIngleTile components
React will automatically re-render the SingleTile components when the state of the parent component changes. By calling this.isIndustrySelected with the id of the SingleTile component, we compare the id with the id that we have stored in the state. This will thus only be equal for the SingleTile that has been clicked for the last time.
Can you post your parent component code?
It's not so important, but you can save some time by using this ES6 feature:
constructor(props){
super(props);
const {title, desc, svg, id, state} = this.props;
this.state = {
selected: false
}
}
I'm running into a problem getting a child react component to update when its parent stage changes. I have an Editor parent component that sets its state and then updates the state if the component receives an updated schedule (from a graphQL mutation component).
The problem is that componentDidUpdate triggers which does trigger the Modefield to update, but it is before the setState in componentDidUpdate can update the state. This means the child doesn't update. (Note- I know a more idiomatic way is to get rid of state all together, but this way allows a field to both edit and create a new one.)
How can I cause the child to update based on the parent's state change?
export const updateScheduleMutation = gql`
mutation updateScheduleMutation(
$id: ID!
$mode: String
) {
updateSchedule(
id: $id
mode: $mode
) {
id
mode
}
}
`;
class EditorWrapper extends React.Component {
constructor(props) {
super(props);
this.state = { scheduleId: props.scheduleId || '' };
}
render() {
return (
<Mutation mutation={updateScheduleMutation}>
{(updateSchedule, { mutationData }) => <Editor {...data} updateSchedule={updateSchedule} />}
</Mutation>
)
}
}
class Editor extends React.Component {
constructor(props) {
super(props);
const { schedule } = props;
if(schedule === null){
this.state = {
schedule: { mode: schedule.mode || "" }
};
}
}
componentDidUpdate(prevProps) {
if (prevProps.schedule !== this.props.schedule) {
this.setState({ ...this.props.schedule });
}
}
changeInput = (path, input) => {
const { updateSchedule, schedule } = this.props;
const field = path.split('.')[1];
updateSchedule({ variables: { id: schedule.id, [field]: input } });
this.setState({ [path]: input });
};
render() {
return (
<ModeField input={this.state.schedule.input} />
);
}
}
const ModeField = ({input}) => FormControl value={input} />
EDIT: I updated the component to show the higher level graphQL wrapper. The reason why I wanted state in the Editor component is that in the event the graphQL query comes back as null, I set this.state.mode to an empty string, which I then update on change. Then, I would create a new schedule item on submit.
LIFT THE STATE UP! Try to manage the base state of your data in parent component and use the data as props in your component:
You also can try getDerivedStateFromProps, but before check the react blog advices:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
I want to fetch data from server periodically and refresh rows when data is fetched by setState() but the rows doesn't re-render after setState().
constructor(props) {
super(props);
this.state = {
rows: []
}
this.refreshList = this.refreshList.bind(this);
}
refreshList() {
req.get('/data').end(function (error, res) {
// type of res is array of objects
this.setState({
rows: res
});
});
}
// call this method on button clicked
handleClick() {
this.refreshList();
}
render() {
return(
<div>
<button onClick={this.handleClick}>Refresh List</button>
<Table rows={this.state.rows}/>
</div>
);
}
when call refreshList() new feteched data doesn't render.
My table component is:
// Table component
export class Table extends Component {
constructor(props) {
super(props);
this.state = {
rows: props.rows
}
}
render() {
return (
<div>
{this.state.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
Thanks a lot for your help. How can I refresh list on click button?
Your table component never changes its state after the construction. You can fix it easily by updating the state from new props:
export class Table extends Component {
constructor(props) {
super(props);
this.state = {
rows: props.rows
}
}
componentWillReceiveProps(newProps) {
this.setState({
rows: newProps.rows
});
}
render() {
return (
<div>
{this.state.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
However, if your table component is so simple, you can make it stateless and use props directly without setState():
export class Table extends Component {
render() {
return (
<div>
{this.props.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
Note there is no need for constructor now. We could actually make it a functional component.
Use an arrow function:
req.get('/data').end((error, res)=> {
// type of res is array of objects
this.setState({
rows: res
});
});
With the ES5 style callback function, the context of this gets lost.
You could also bind this directly to a local variable, i.e., var that = this and stick with the function syntax, but I think most would agree what the ES6 arrow syntax is nicer.
Im trying to make a search function that renders the name of the people that is matched in a search text input.
The problem is that I set the state to the items that match the search, and then the initial state is lost so no more searching can be done since the state will be empty. So how do I "fill up" the state each time?
Or maybe there is some other way without actually setting the state that im not aware of.
I tried to fix this with an attempt to reset to initial state when the handleSearch function is called right before the filter but that doesnt work.
import React from 'react';
import Header from './Header';
import peopleData from '../persons.json';
class App extends React.Component {
constructor(){
super();
this.handleSearch = this.handleSearch.bind(this);
this.state = {
people: peopleData
}
}
handleSearch(wordToMatch){
this.setState({ people: peopleData }); //Attempt to reset to initial state
const result = this.state.people.filter(d => {
const regex = new RegExp(wordToMatch, 'gi');
return d.Name.match(regex);
});
this.setState({ people: result })
}
render() {
const list = this.state.people.map((d, i) => <li key={i}>{d.Name}</li>);
return (
<div className="myApp">
<Header
tagline={"testing"}
handleSearch={this.handleSearch}
/>
<ul className="contentBody">
{list}
</ul>
</div>
)
}
}
export default App;
Component with the search input:
import React from 'react';
class Header extends React.Component {
render() {
return (
<header>
<input
type="text"
placeholder={this.props.tagline}
ref={(input) => this.searchInput = input}
onChange={() => this.props.handleSearch(this.searchInput.value)}
>
</input>
</header>
)
}
}
export default Header;
How my data looks like
[
{
"Name": "Adam",
"Born": 1971
},
{
"Name": "Bob",
"Born": 1999
},
etc etc for 20 more names
The setState function won't immediately update the state object. So when you reference this.state.people, it will reference the state prior to the setState call. You can update your code to:
handleSearch(wordToMatch) {
const result = peopleData.filter(d => {
const regex = new RegExp(wordToMatch, 'gi');
return d.Name.match(regex);
});
this.setState({
people: result
})
}
In the handleSearch set the state for the searchString variable. Then in the render method, instead of simply mapping the state, you first filter the people list, and that result is what you map.
Change:
const list = this.state.people.map((d, i) => <li key={i}>{d.Name}</li>);
into this:
const list = this.state.people.filter(d => {
const regex = new RegExp(this.state.searchString, 'gi');
return d.Name.match(regex);
}).map((d, i) => <li key={i}>{d.Name}</li>);
This way, the list in the state is left unaltered, and you filter when rendering.