Why isn't the component child rendered? - javascript

I have written the following code in APP.js component:
import React from "react";
import Exam from "./exam.js";
export default function App() {
return (
<Exam>
<h1>hashemi</h1>
</Exam>
);
}
And I have written the following code in exam.js component:
import React from "react";
const Exam = ({child}) => {
return (
<div>
<p>parastoo</p>
{child}
</div>
);
};
export default Exam;
But the output shows this:
parastoo
What is the problem? Why doesn't the child <h1> render?

Child components are passes via the children prop to the component, even if there is only a single child:
const Exam = ({children}) => {
return (
<div>
<p>parastoo</p>
{children}
</div>
);
};

It's called props.children. Read from the documentation section Containment.
const Exam = (props) => {
return (
<div>
<p>parastoo</p>
{props.children}
</div>
);
};
I hope this helps!

In React, you can pass props, or properties, to child components. Say you have an App component which renders a child component called CurrentDate which is a stateless functional component. You can pass CurrentDate a date property by writing:
const CurrentDate = (props) => {
return (
<div>
{ /* change code below this line */ }
<p>The current date is: {props.date} </p>
{ /* change code above this line */ }
</div>
);
};
Calender is a parent Component, you can pass Calender a date property by writing
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* change code below this line */ }
<CurrentDate date={Date()}/>
{ /* change code above this line */ }
</div>
);
}
};

Related

How to properly render Component after this.setState in React

I have this React component
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
this.setState({
resources: this.props.location.resources,
});
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
It gets the resources from the Link component, and that works fine. If I check out the state of the Component from the dev tools, the state looks right. And I thought with my logic this should work. So firstly, the state is empty, the component gets rendered, since the state is empty it doesn't render any components. Then, setState gets called, it gets all the resources and saves them into the state, and then the component would re-render, and it should work, but it doesn't. I'm getting a TypeError: Cannot read property 'map' of undefined error. What is the correct way to do this and how do I fix this?
Try this code:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: this.props && this.props.location && this.props.location.resources?this.props.location.resources:[],
};
}
componentDidMount() {
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or use directly props
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{
this.props && this.props.location &&
this.props.location.resources
?this.props.location.resources.map(res => (
<div>test</div>
))
:null
}
</div>
);
}
}
Or use componentWillReceiveProps or getDerivedStateFromProps life cycle methods.
Check this.props.location.resources is array.
See more: https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607
For first check is this.props.location.resources array, or if data type changes you can add checking, you can use lodash isArray or with js like this:
import React, { Component } from "react";
export default class ResourceForField extends Component {
constructor() {
super();
this.state = {
resources: [],
};
}
componentDidMount() {
// get the resources from the Link props and save it into the state
Array.isArray(this.props.location.resources) {
this.setState({
resources: this.props.location.resources,
});
}
}
// This component gets the id of current learningField from the url
// and the rest(like the resources) from the Link component
render() {
return (
<div>
{this.state.resources.map(res => (
<div>test</div>
))}
</div>
);
}
}
Or you can just use hooks like this:
import React, { useState, useEffect } from "react";
export default function ResourceForField({location}) {
const [ resources, setResources ] = useState([]);
useEffect(() => {
if (location && Array.isArray(location.resources)) {
setResources(location.resources)
}
}, [location]);
return (
<div>
{resources.map(res => (
<div>test</div>
))}
</div>
);
}
If the internal state of ResourceForField doesn't change and always equals to its prop, you shouldn't save the prop in the state. You can instead create a pure functional component.
Also note that there's nothing preventing you from initializing the state from the props in constructor method. i.e. you're not required to wait for the component to mount in order to access the props.
So, I'd write the following component for ResourceForField:
function ResourceForField({resources = []}) {
return (
<div>
{
resources.map(res => (<div>test</div>))
}
</div>
);
}

Fill input with buttons values in ReactJs

I'm building a small application in ReactJS, it consists of a grid of buttons with letters as values, what I need to do, is to fill an input field with the letters of the buttons clicked, basically like a keyboard.
I've built the grid with the buttons, each button has a letter, but I'm not sure on how I should code the next part; each button should have two stated, either clicked or not, if its clicked, the letter will appear on the input, if clicked again, it should be removed.
These are my components right now:
Square
import React from "react"
class Square extends React.Component {
render() {
return (
<button type="button" className="square">{this.props.letter}</button>
);
}
}
export default Square;
Input Component
import React from 'react';
class Clear extends React.Component {
render() {
return (
<div className="clear-btn">
<button><span>Clear Word</span><span className="cross-icon">X</span></button>
<input className="cust-input" type="text"/>
</div>
);
}
}
export default Clear;
Main App Component
function App() {
return (
<div className="App">
<div className="container">
<div className="letters">
{LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter}/>)}
</div>
<div className="controls">
<Clear />
</div>
</div>
</div>
);
}
export default App;
If anyone can help me on this it would be great, I don't know what would be a good way to get the value of the button and adding it on the input when clicked.
I imagine this would have to be done with events or something like that, quite honestly I'm just starting to learn React and I'm not sure on how I should arrange all the components so they work together.
This is how the app looks as of now:
Consider the following code, also here is the sandbox for you:
https://codesandbox.io/s/6xpzvpno1r
This is our App component. We will populate the buttons here and give each button its letter, passing it through props. We also give each Button component a state-updater function that will update the state of our App component.
import React from 'react'
import ReactDOM from 'react-dom'
import Button from './Button'
import Input from './Input'
class App extends React.Component {
state = {
letters: ['a', 'b', 'c', 'd', 'e'],
value: '',
}
updateValue = letter => {
console.log('ran')
this.setState({
value: this.state.value + letter,
})
}
createButtons = () => {
const letters = this.state.letters
return letters.map(letter => (
<Button letter={letter} updateValue={this.updateValue} />
))
}
render() {
return (
<div>
{this.createButtons()}
<Input value={this.state.value} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
Button component: here we keep call that state-updating function on click and keep track if it has been clicked before.
import React from 'react'
class Button extends React.Component {
state = {
clicked: false,
}
handleOnClick = () => {
if (!this.state.clicked) {
this.props.updateValue(this.props.letter)
this.setState({
clicked: true,
})
}
}
render() {
return (
<button onClick={this.handleOnClick} disabled={this.state.clicked}>
{this.props.letter}
</button>
)
}
}
export default Button
Lastly we have our Input component: which just consumes the value from the parent App component.
import React from 'react'
class Input extends React.Component {
render() {
return <input value={this.props.value} />
}
}
export default Input
Let me know if this is helpful to you. I feel like this essentially provides the principles you need to get your code to work.
Let's break what you want into steps:
Clicking a component should send its letter to the parent component.
That array of letters should be stored in the parent component
The input's value should be the value of that array, but as a string.
1) For the Square component to be clickable, it needs an onClick handler. On click, we'll call a function that's passed into Square from the parent component:
import React from "react"
class Square extends React.Component {
render() {
const { handleClick, letter } = this.props;
return (
<button type="button" className="square" onClick={() => handleClick(letter)}>
{this.props.letter}
</button>
);
}
}
export default Square;
2) Main app controller needs a state property to store the letters that get clicked so we can keep track of them. We also need to pass these letters to the input component.
class App extends Component {
constructor(props) {
super(props);
this.state = {
clickedLetters: [],
};
}
saveClickedLetter(letter) {
const { clickedLetters } = this.state;
const cloneOfClickedLetters = clickedLetters;
cloneOfClickedLetters.push(letter);
this.setState({ clickedLetters: cloneOfClickedLetters });
}
render() {
const { clickedLetters } = this.state;
return (
<div className="App">
<div className="container">
<div className="letters">
{LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter} handleClick={this.saveClickedLetter}/>)}
</div>
<div className="controls">
<Clear clickedLetters={clickedLetters.length > 0 && clickedLetters.join()}/>
</div>
</div>
</div>
)
}
}
export default App;
Finally, let's pass in the clickedLetters prop to input's value attribute:
import React from 'react';
class Clear extends React.Component {
render() {
const { clickedLetters } = this.props;
return (
<div className="clear-btn">
<button><span>Clear Word</span><span className="cross-icon">X</span></button>
<input value={clickedLetters} className="cust-input" type="text"/>
</div>
);
}
}
export default Clear;

Rendering a canvas object received from props

Good day!
I am new to React and html2canvas. I am making an app which will take "screenshots" of my DOM using html2canvas then store it to an array of screenshots which will then be also rendered on the screen.
I am storing each <canvas> object received from the html2canvas promise to an array then pass it to my ScreenshotsContainer component which passes the array to the Screenshots component. The Screenshots component will then map the array of <canvas> objects to individual Screenshot components.
In App.js, I am calling the html2canvas function then pass the array to ScreenshotsContainer component
import React, { Component } from 'react';
import ScreenshotsContainer from './containers/ScreenshotsContainer/ScreenshotsContainer'
import html2canvas from 'html2canvas';
import './App.css';
class App extends Component {
state = {
canvasArray: []
}
getScreenshotHandler = () => {
console.log("[Canvas Array from state length:]" + this.state.canvasArray.length)
let canvasArray = this.state.canvasArray;
html2canvas(document.body).then((canvas) => {
canvasArray.push(canvas)
});
console.log("[Canvas Object value: ]" + canvasArray);
this.setState({ canvasArray: canvasArray })
}
render() {
return (
<React.Fragment>
<button onClick={this.getScreenshotHandler}>Get Screenshot</button>
<ScreenshotsContainer canvasArray={this.state.canvasArray} />
</React.Fragment>
);
}
}
export default App;
The ScreenshotsContainer component will pass the received array to the Screenshots component:
import React, { Component } from 'react';
import './ScreenshotsContainer.css'
import Screenshots from '../../components/Screenshots/Screenshots';
class ScreenshotsContainer extends Component {
render() {
return (
<div className="ScreenshotsContainer">
<Screenshots canvasArray={this.props.canvasArray} />
</div>
);
}
}
export default ScreenshotsContainer;
The Screenshots component will map the array and pass each canvas object to the Screenshot component:
import React, { Component } from 'react';
import Screenshot from './Screenshot/Screenshot';
class Screenshots extends Component {
render() {
const screenshots = this.props.canvasArray.map(canvas => {
return (
<Screenshot
key={Math.random}
canvasObj={canvas}
/>
)
})
return (
<React.Fragment>
{screenshots}
</React.Fragment>
);
}
}
export default Screenshots;
Here is the Screenshot component
import React from 'react';
import './Screenshot.css';
const screenshot = (props) => (
<div className="Screenshot" >
<canvas ref={props.canvasObj} style={{
width: '10%',
height: '10%'
}} />
</div>
);
export default screenshot;
What I actually get when pressing the button:
Actual screenshot of my result
I was wondering which part went wrong. Any help would be appreciated.
This particular library works in a specific way (looks like it's doing a lot of "magic" under the hood - you should look at the source code here more specifically the renderer folder inside src)
Saving the canvas to the state inside of an array (the correct react way of doing things) will be a problem as it saves it as a complex object with many methods etc... and we can not render objects... This lib was not written with React in mind...
The code sample below is a simple implementation in React...
Here is a live demo: https://codesandbox.io/s/9y24vwn1py
import React, { Component } from 'react';
import html2canvas from 'html2canvas';
class App extends Component {
constructor(props) {
super(props);
this.captureRef = React.createRef();
this.displayRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.displayRef.current.appendChild(canvas),
);
};
render() {
return (
<div>
<div ref={this.captureRef}>
<h2>This enitre div will be captured and added to the screen</h2>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below</h5>
<div ref={this.displayRef} />
</section>
</div>
);
}
}
export default App;
EDIT: based on the comment below here is yet another workaround:
class App extends Component {
constructor(props) {
super(props);
this.state = { canvasArray: [] };
this.captureRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.setState({
canvasArray: [canvas.toDataURL(), ...this.state.canvasArray],
}),
);
};
renderCanvas = () => {
return this.state.canvasArray.map((canvas, i) => {
return <img key={i} src={canvas} alt="screenshot" />;
});
};
render() {
return (
<div className="wrapper">
<div ref={this.captureRef}>
<p>This enitre div will be captured</p>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below:</h5>
{this.renderCanvas()}
</section>
</div>
);
}
}
Link to live demo: https://codesandbox.io/s/1r213057vq

Insert Props in a React class component

I'm working with ReactJS and the npm module "react-media-query-hoc", everything is fine when I use functional components to export them with: withMedia().
Now I need to use a class component but I'm not doing it right.
This is from "react-media-query-hoc" docs:
import { withMedia } from 'react-media-query-hoc';
const MyComponent = ({ media, ...props}) => (
if(media.tablet || media.mobile) {
..
return (
<div>
Mobile and Tablet View
</div>
)
}
return (
<div>
Other View
</div>
)
);
export const BaseMyComponent = MyComponent;
export default withMedia(MyComponent);
I need to transform this to a class Component, please some Help will be great :)
https://www.npmjs.com/package/react-media-query-hoc
It should be something like this:
import { withMedia } from 'react-media-query-hoc';
import React from 'react';
class MyComponent extends React.Component {
render(){
if(this.props.media.tablet || this.props.media.mobile) {
...
return (
<div>
Mobile and Tablet View
</div>
)
}
return (
<div>
Other View
</div>
)
}
}
export const BaseMyComponent = MyComponent;
export default withMedia(MyComponent);

How to send this.state from a component to relay root container

I want to change my root query parameter based on the this.state.eventid which is a child component, but I have no clue how to get props to relay root container. I started based on relay-starter-kit.
I have React component that has a dropdown menu, and onSelect it setStates for eventId
renderAttend() {
if (this.props.groups != null && this.state.success != true) {
var events = this.props.events.map(function(event){
var boundSelect = () => {this.setState({eventid:event.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={event.nodes[0].id} onSelect={boundSelect.bind(this)}>{event.nodes[0].properties.summary} / {event.nodes[0].properties.start}</MenuItem>
},this)
var teams = this.props.groups.map(function(team){
var boundSelect = () => {this.setState({teamid:team.nodes[0].id})}
return <MenuItem style={{fontSize:20}}eventKey={team.nodes[0].id} onSelect={boundSelect.bind(this)}>{team.nodes[0].properties.name}</MenuItem>
},this)
return (
<div>
<ButtonGroup>
<DropdownButton style={{padding:"15px",fontSize:20}}title="Events" id="bg-vertical-dropdown-2">
{events}
</DropdownButton>
<DropdownButton style={{padding:"15px",fontSize:20,marginLeft:"5px"}} title="Groups" id="bg-vertical-dropdown-2">
{teams}
</DropdownButton>
</ButtonGroup>
</div>
)
}
}
I want to use this state to somehow change my root query...
my approute...
import Relay from 'react-relay';
export default class extends Relay.Route {
static paramDefinitions = {
eventId: {required: false}
};
static queries = {
Event : () => Relay.QL`query{eventState(eventId:$eventId)}`,
};
static routeName = 'AppHomeRoute';
}
and my app.js
import 'babel-polyfill';
import App from './components/App';
import AppHomeRoute from './routes/AppHomeRoute';
import React from 'react';
import ReactDOM from 'react-dom';
import Relay from 'react-relay';
ReactDOM.render(
<Relay.RootContainer
Component={App}
route= {new AppHomeRoute}
renderLoading={function() {
return <div style= {{display:"flex",justifyContent:"center",marginTop:"55px"}}> <h1>Loading...</h1></div>;
}}
renderFailure={function(error, retry) {
return (
<div>
<h1>Click Refresh</h1>
</div>
);
}}
/>,
document.getElementById('root')
);
Now I want to this.state.eventid from the react component to update my root query, but I have no idea how to pass data from child component to react root.container. I do not want to use react-router for this :)
p.s. this.props.events were passed to me by an ajax call so they are not saved in relay/graphql data.
For such a case, the better thing to do is to wrap your root query into a story like
{
store {
events(eventId:$eventId)
}
}
So in the root query you only have
export default class extends Route {
static queries = {
app:() => Relay.QL`query { store }`
};
static routeName = "AppRoute";
}
And in the page you create a fragemnt like
let RelayApp = createContainer(SomeComponent, {
initialVariables: {
eventId: null
},
fragments: {
app: () => Relay.QL `
fragment on Store {
id
events(eventId: $eventId) {
pageInfo {
hasNextPage
}
edges {
cursor
node {
name
...
}
}
}
}
`,
},
});
export
default RelayApp;
For the child component, you set the eventId and onChange event handler as props from parent component. And in the parent componet you implement the event handler and call this.props.setVariables({eventId: someVal}) like
// Child Component
export default class Menu extends Component {
render() {
return(
<ul>
<li onClick={() => this.props.selectItem(val)}>{val}</li>
...
</ul>
)
}
}
// Parent Component
class Main extends Component {
_selectItem = (val) => {
this.props.relay.setVariables({eventId: val});
}
render() {
return(
<div>
<Menu selectItem={() => this._selectItem}/>
</div>
)
}
}
let RelayApp = ...
export default Main
Hope this will help.
There is no easy way to solve this. Either use react-router-relay or nest your query like this and use this.props.relay.setVariables()
viewer {
eventState(eventid:$eventid) {
data
}
}

Categories

Resources