pass default value from graphql query in apollo to child - reactjs

I am trying to pass graphql query value to my React component from the parent component's state. My method isn't work working because it's loading and isn't called again after it is done loading. Any recommendations on how to do this?
class Parent extends Component {
constructor(props) {
super(props)
const defaultSelectedId = (this.props.query && !this.props.query.loading) ?
this.props.query.defaultId : ''
this.state = {
id: defaultSelectedId
}
render() {
return(
<Child selectedId={this.state.id} />
)}}

Add
componentWillReceiveProps(nextProps) {
const defaultSelectedId = (nextProps.query && !nextProps.query.loading) ?
nextProps.query.defaultId : ''
this.setState ({
id: defaultSelectedId
})
}

Related

ReactJS Change Sibling State via Parent

My React structure is
- App
|--SelectStudy
|--ParticipantsTable
In SelectStudy there is a button whose click triggers a message to its sibling, ParticipantsTable, via the App parent. The first Child->Parent transfer works. But how do I implement the second Parent->Child transfer? See questions in comments.
App
class App extends Component {
myCallback(dataFromChild) {
// This callback receives changes from SelectStudy Child Component's button click
// THIS WORKS
alert('SelectStudy Component sent value to Parent (App): ' + dataFromChild.label + " -> " + dataFromChild.value);
// QUESTION: How to Update State of ParticipantsTable (SelectStudy's Sibling) next?
// ........................................................
}
render() {
return (
<div className="App">
<SelectStudy callbackFromParent={this.myCallback}></SelectStudy>
<ParticipantsTable></ParticipantsTable>
</div>
);
}
SelectStudy
class SelectStudy extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: [],
selectedStudy: null,
isButtonLoading: false
};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
render() {
const { error, isLoaded, items, itemsForReactSelect, selectedStudy, isButtonLoading } = this.state;
return <Button onClick={this.handleButtonClick}>Search</Button>;
}
handleButtonClick = () => {
this.props.callbackFromParent(this.state.selectedStudy);
}
}
ParticipantsTable - this needs to receive a certain variable, e.g. study in its State
class ParticipantsTable extends React.Component {
constructor(props) {
//alert('Constructor');
super(props);
// Initial Definition of this component's state
this.state = {
study: null,
items: [],
error: null
};
}
// THIS METHOD IS AVAILABLE, BUT HOW TO CALL IT FROM App's myCallback(dataFromChild)?
setStudy = (selectedStudy) => {
this.setState({study: selectedStudy});
}
render() {
return ( <div>{this.state.study}</div> );
}
}
The state should live definitively at the App level, not in the child. State needs to live one level above the lowest common denominator that needs access to it. So if both SelectStudy and ParticipantsTable need access to the same bit of state data, then it must live in their closest common ancestor (or above).
This is a core concept of React, known as "lifting state up", so much so that it has its own page in the official React documentation.
In your case, it would look something like this. Notice how state lives in only one place, at the <App /> level, and is passed to children via props.
import React from 'react';
class App extends React.Component {
// State lives here at the closest common ancestor of children that need it
state = {
error: null,
isLoaded: false,
items: [],
selectedStudy: null,
isButtonLoading: false
};
myCallback = (dataFromChild) => {
this.setState(dataFromChild);
};
render() {
return (
<div className="App">
{/* State is passed into child components here, as props */}
<SelectStudy data={this.state} callbackFromParent={this.myCallback}></SelectStudy>
<ParticipantsTable study={this.state.selectedStudy} />
</div>
);
}
}
class SelectStudy extends React.Component {
handleButtonClick = () => {
// Here we execute a callback, provided by <App />, to update state one level up
this.props.callbackFromParent({ ...this.props.selectedStudy, isButtonLoading: true });
};
render() {
const { error, isLoaded, items, itemsForReactSelect, selectedStudy, isButtonLoading } = this.props.data;
return <Button onClick={this.handleButtonClick}>Search</Button>;
}
}
// This component doesn't need to track any internal state - it only renders what is given via props
class ParticipantsTable extends React.Component {
render() {
return <div>{this.props.study}</div>;
}
}
I think what you need to understand is the difference between state and props.
state is internal to a component while props are passed down from parents to children
Here is a in-depth answer
So you want to set a state in the parent that you can pass as props to children
1 set state in the parent
this.state = {
value: null
}
myCallback(dataFromChild) {
this.setState({value: dataFromChild.value})
}
2 pass it as a prop to the children
class ParticipantsTable extends React.Component {
constructor(props) {
super(props);
this.state = {
study: props.study,
items: [],
error: null
};
}
Also, although not related to your question, if you learning React I suggest moving away from class-based components in favour of hooks and functional components as they have become more widely used and popular recently.

React+ sending entire state object to another component

pretty simple but confused,
i have one parent component with state as
this is my
this.state = {
todoName: "",
todoList: [],
"isAvail":true,
"IsArchive":false
};
and inside my parent component i am calling child component
<ChildComponent data= {this.state} />
but in childCompoenent inside the render i try to call like
const isAvail = this.props.data.isAvail;
const isArchieve = this.props.data.isArchieve;
and inside retrun if i call like this am {isAvail} am not getting and am getting error
Objects are not valid as a React child (found: object with keys {todoName, todoList, isAvail, IsArchive}). If you meant to render a collection of children, use an array instead.
But somehow i need to get full object..How is it possible
You may call like this one
render() {
const {isAvail, isArchieve} = this.props.data;
return(<div><p>{isAvail}</p></div>);
}
class Parent extends React.Component{
constructor(props){
super(props)
this.state= {
todoName: "passed from parent", todoList: [], "isAvail":true, "IsArchive":false
}
}
render(){
return <Child data={this.state}/>
}
}
const Child = (props) => <div>{props.data.todoName}{console.log(props)}</div>
Live Demo
Do you know ContextAPI & you want to send data without using props drilling useContextAPI. In some cases we can use state values in to childcomponents also in using context API.
In your parent component create context
const ParentState = React.createContext()
render() {
<ParentState.Provider value={{ data: this.state }}>
<Childcomponent/>
</ParentState.Provider>
}
In Child component import Context and use
render() {
return(
<ParentState.Consumer>
{
parentState => (<div><p>{parentState.data.isAvail}</p></div>)
}
</ParentState.Consumer>
)
}

How do I limit the user to only selecting one component?

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
}
}

React- How to update child prop based on parent state

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

React child component can't get props.object

My parent component is like this:
export default class MobileCompo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
datasets: {}
};
this.get_data = this.get_data.bind(this);
}
componentWillMount() {
this.get_data();
}
async get_data() {
const ret = post_api_and_return_data();
const content={};
ret.result.gsm.forEach((val, index) => {
content[val.city].push()
});
this.setState({data: ret.result.gsm, datasets: content});
}
render() {
console.log(this.state)
// I can see the value of `datasets` object
return (
<div>
<TableElement dict={d} content={this.state.data} />
<BubbleGraph maindata={this.state.datasets} labels="something"/>
</div>
)
}
}
child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
console.log(this.props);
// here I can't get this.props.maindata,it's always null,but I can get labels.It's confusing me!
}
componentWillMount() {
sortDict(this.props.maindata).forEach((val, index) => {
let tmpModel = {
label: '',
data: null
};
this.state.finalData.datasets.push(tmpModel)
});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I tried many times,but still don't work,I thought the reason is about await/async,but TableElement works well,also BubbleGraph can get labels.
I also tried to give a constant to datasets but the child component still can't get it.And I used this:
this.setState({ datasets: a});
BubbleGraph works.So I can't set two states at async method?
It is weird,am I missing something?
Any help would be great appreciate!
Add componentWillReceiveProps inside child componenet, and check do you get data.
componentWillReceiveProps(newProps)
{
console.log(newProps.maindata)
}
If yes, the reason is constructor methos is called only one time. On next setState on parent component,componentWillReceiveProps () method of child component receives new props. This method is not called on initial render.
Few Changes in Child component:
*As per DOC, Never mutate state variable directly by this.state.a='' or this.state.a.push(), always use setState to update the state values.
*use componentwillrecieveprops it will get called on whenever any change happen to props values, so you can avoid the asyn also, whenever you do the changes in state of parent component all the child component will get the updates values.
Use this child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
}
componentWillReceiveProps(newData) {
let data = sortDict(newData.maindata).map((val, index) => {
return {
label: '',
data: null
};
});
let finalData = JSON.parse(JSON.stringify(this.state.finalData));
finalData.datasets = finalData.datasets.concat(data);
this.setState({finalData});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}

Resources