As the title says, I can not figure out why the return of a function doesn't show on screen.
The object words.First.wordsdata is holding key-value pairs
import React from "react";
const WordList = ({ words }) => {
return (
<div>
{ words &&
Object.entries(words.First.wordsdata).forEach(([key, value]) => {
return(<div>{key} - {value}</div>);
})
}
</div>
);
};
export default WordList;
If I change the return to log it out, then
this one does show everything correctly in dev tools
return(console.log(key, value));
To give a full view, this is the file that calls the component
class Words extends Component {
render() {
//console.log(this.props)
const { words, auth } = this.props;
if (!auth.uid) return <Redirect to="/" />;
return (
<div>
<WordList words={words} />
</div>
);
}
}
I tried changing the return to simple HTML and It still doesn't show anything
Thank you for the answers, indeed changing .forEach to .map did the trick.
Related
I'm currently learning React and i am working through 'The Road to React' by Robin Wieruch.
I've just refactored some code from a stateful to a functional stateless component like so:
function Search(props) {
const { value, onChange, children } = props;
return (
<form>
{children} <input
type="text"
value={value}
onChange={onChange}
/>
</form>
);
}
Gets Refactored to:
const Search = ({ value, onChange, children }) => {
<form>
{children} <input
type="text"
value={value}
onChange={onChange}
/>
</form>
}
However nothing is rendering anymore. Are functional stateless components called the same was as stateful ones?
This is how I'm calling the Search component in the App class:
render() {
const { searchTerm, list } = this.state;
return (
<div className="App">
<Search
value = { searchTerm }
onChange = { this.onSearchChange }
>
Search
</Search>
<Table
list = { list }
pattern = { searchTerm }
onDismiss = { this.onDismiss }
/>
</div>
)
I'm not receiving an error at all, so i'm not getting much that's pointing me in the right direction, i'm hoping i'm just missing something silly.
Thanks in advance!
In both cases, it's a stateless function only as there's no state and it's not an class component either.
1st case is working correctly because it's returning the element with the return keyword.
2nd refactored case is also correct but you are not returning anything you need to return the element for it to be rendered.
return example
const func = () => {
... // any more calculations or code
return ( // you are returning the element here
<div>
...
</div>
)
}
If there's no calculation or any additional code and you have to return only an element you can directly return it by using (...) instead of {...} as follows
const func = () => ( // you are directly returning element
<div>
...
</div>
)
PS: for more info you can check into arrow functions
I have a small part of my new React app which contains a block of text, AllLines, split into line-by-line components called Line. I want to make it work so that when one line is clicked, it will be selected and editable and all other lines will appear as <p> elements. How can I best manage the state here such that only one of the lines is selected at any given time? The part I am struggling with is determining which Line element has been clicked in a way that the parent can change its state.
I know ways that I can make this work, but I'm relatively new to React and trying to get my head into 'thinking in React' by doing things properly so I'm keen to find out what is the best practice in this situation.
class AllLines extends Component {
state = {
selectedLine: 0,
lines: []
};
handleClick = (e) => {
console.log("click");
};
render() {
return (
<Container>
{
this.state.lines.map((subtitle, index) => {
if (index === this.state.selectedLine) {
return (
<div id={"text-line-" + index}>
<TranscriptionLine
lineContent={subtitle.text}
selected={true}
/>
</div>
)
}
return (
<div id={"text-line-" + index}>
<Line
lineContent={subtitle.text}
handleClick={this.handleClick}
/>
</div>
)
})
}
</Container>
);
}
}
class Line extends Component {
render() {
if (this.props.selected === true) {
return (
<input type="text" value={this.props.lineContent} />
)
}
return (
<p id={} onClick={this.props.handleClick}>{this.props.lineContent}</p>
);
}
}
In your case, there is no really simpler way. State of current selected Line is "above" line collection (parent), which is correct (for case where siblings need to know).
However, you could simplify your code a lot:
<Container>
{this.state.lines.map((subtitle, index) => (
<div id={"text-line-" + index}>
<Line
handleClick={this.handleClick}
lineContent={subtitle.text}
selected={index === this.state.selectedLine}
/>
</div>
))}
</Container>
and for Line component, it is good practice to use functional component, since it is stateless and even doesn't use any lifecycle method.
Edit: Added missing close bracket
'Thinking in React' you would want to give up your habit to grab DOM elements by their unique id ;)
From what I see, there're few parts missing from your codebase:
smart click handler that will keep only one line selected at a time
edit line handler that will stick to the callback that will modify line contents within parent state
preferably two separate components for the line capable of editing and line being actually edited as those behave in a different way and appear as different DOM elements
To wrap up the above, I'd slightly rephrase your code into the following:
const { Component } = React,
{ render } = ReactDOM
const linesData = Array.from(
{length:10},
(_,i) => `There goes the line number ${i}`
)
class Line extends Component {
render(){
return (
<p onClick={this.props.onSelect}>{this.props.lineContent}</p>
)
}
}
class TranscriptionLine extends Component {
constructor(props){
super(props)
this.state = {
content: this.props.lineContent
}
this.onEdit = this.onEdit.bind(this)
}
onEdit(value){
this.setState({content:value})
this.props.pushEditUp(value, this.props.lineIndex)
}
render(){
return (
<input
style={{width:200}}
value={this.state.content}
onChange={({target:{value}}) => this.onEdit(value)}
/>
)
}
}
class AllLines extends Component {
constructor (props) {
super(props)
this.state = {
selectedLine: null,
lines: this.props.lines
}
this.handleSelect = this.handleSelect.bind(this)
this.handleEdit = this.handleEdit.bind(this)
}
handleSelect(idx){
this.setState({selectedLine:idx})
}
handleEdit(newLineValue, lineIdx){
const linesShallowCopy = [...this.state.lines]
linesShallowCopy.splice(lineIdx,1,newLineValue)
this.setState({
lines: linesShallowCopy
})
}
render() {
return (
<div>
{
this.state.lines.map((text, index) => {
if(index === this.state.selectedLine) {
return (
<TranscriptionLine
lineContent={text}
lineIndex={index}
pushEditUp={this.handleEdit}
/>
)
}
else
return (
<Line
lineContent={text}
lineIndex={index}
onSelect={() => this.handleSelect(index)}
/>
)
})
}
</div>
)
}
}
render (
<AllLines lines={linesData} />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Evening/Morning All
So, for the 2nd time in as many days, I'm back on here - hat in hand. I've got this kind of condition to work so well elsewhere but here I just can't get the 'loading' div to disappear once the 'pageData' has been fully downloaded. I tried it already with if/else (which I don't normally use in this case). If anyone could cast a 2nd set of eyes over this for me I'd be very thankful.
import DataStore from "flux/stores/DataStore.js";
const ContentData = ({ pageData }) => {
if (!pageData) {
// evaluates to true if pageData is null
return <div>Loading...</div>;
}
return (
<div>
<h2>Homepage template</h2>
<h1>{pageData.title.rendered}</h1>
<div
className="content"
dangerouslySetInnerHTML={{ __html: pageData.content.rendered }}
/>
<div>{pageData.acf.text}</div>
</div> // render content
);
};
class Home extends React.Component {
render() {
let pageData = DataStore.getPageBySlug("home");
return (
<div>
<ContentData />
</div>
);
}
}
export default Home;
Ths data is definitely coming through from the Wordpress API, I can view it all in the console.
Thanks in advance
Terry
Here's the 'getPageBySlug' function
// Returns a Page by provided slug
getPageBySlug(slug){
const pages = this.getState().data.pages;
return pages[Object.keys(pages).find((page, i) => {
return pages[page].slug === slug;
})] || {};
}
}
I have a Dashboard component that renders an array of cards with data fetched from a backend server. Users can create additional cards by submitting a form, which then redirects them back to the dashboard page.
My issue is that when the form is submitted, a javascript error 'cannot read property "includes" of undefined' is thrown and the dashboard does not render. If I manually refresh the page, the list renders as expected with the new card. I use Array.includes method to filter the cards based on the filterText state value. Does this error happen because the data has not been fetched when render is called? If so, how can I force the component to wait until there is data before rendering? Please see the components and redux action below.
const CardList = (props) => {
const cards = props.cards.map(({ _id, title}) => {
return (
<Card key={_id} title={title} />
)
});
return (
<div className="container">
<input onChange={ (e) => props.handleChange(e.target.value) } />
<div className="row">
{cards}
</div>
</div>
);
}
export default CardList;
export class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
filterText: ''
}
}
componentDidMount() {
this.props.fetchCards();
}
handleChange = (filterText) => {
this.setState({filterText});
}
render() {
const cardList = this.props.cards.filter(card =>
card.title.includes(this.state.filterText.trim().toLowerCase())
);
return (
<div>
<CardList cards={cardList}
handleChange={filterText => this.handleChange(filterText)} />
</div>
);
}
};
function mapStateToProps({ cards: { cards }}) {
return {
cards,
}
}
export default connect(mapStateToProps, {fetchCards})(Dashboard);
export class SurveyForm extends Component {
render() {
return (
<div>
<form>
<Field component={CardField} type="text"
label={'title'} name={'title'} key={'title'} />
<Button type="submit" onClick={() => submitCard(formValues, history)}>Next</Button>
</form>
</div>
);
}
}
REDUX ACTION DISPATCHER:
export const submitCard = (values, history) => async dispatch => {
const res = await axios.post('/api/cards', values);
try {
dispatch({ type: SUBMIT_CARD_SUCCESS, payload: res.data });
dispatch({ type: FETCH_USER, payload: res.data })
}
catch(err) {
dispatch({ type: SUBMIT_CARD_ERROR, error: err });
}
history.push('/cards');
}
Similar to what #JasonWarta mentioned, it's worth noting that React does not render anything when false, null, or undefined is returned, so you can usually use && to be more succinct than using the conditional ("ternary") operator:
render() {
return this.props.cards && (
<div>
<CardList
cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
handleChange={filterText => this.handleChange(filterText)}
/>
</div>
);
}
Because && short-circuits, the latter part won't be evaluated so you can avoid TypeErrors, and the component will also render no content (same as when you return null).
I've used ternary operators in this kind of situation. You may need to adjust the check portion of the pattern, depending on what your redux pattern is returning. null value is returned if this.props.cards is falsey.
render() {
return (
{this.props.cards
?
<div>
<CardList
cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
handleChange={filterText => this.handleChange(filterText)}
>
</CardList>
</div>
:
null
}
);
}
As an alternative to other answers you can return something else suitable if there is no data in your render function with an if statement. I prefer moving functions like your filter one outside of render. Maybe one other (better?) approach is doing that filter in your mapStateToProps function.
Also, if I'm not wrong you don't need to pass anything to your handleChange function. Because you are getting filterText back from CardList component then setting your state.
cardList = () => this.props.cards.filter(card =>
card.title.includes(this.state.filterText.trim().toLowerCase()));
render() {
if ( !this.props.cards.length ) {
return <p>No cards</p>
// or return <SpinnerComponent />
}
return (
<div>
<CardList cards={this.cardList()}
handleChange={this.handleChange} />
</div>
);
}
I have the following code. It doesn't render the fullRecipe items at all but i see nothing wrong in here. I have trouble learning this framework and after asking nobody knows what's happening... Do you see what's wrong?
Thanks
class Index extends React.Component {
constructor() {
super();
}
render() {
var list_results = this.props.recipes.map(function(recipe, index){
console.log(index); //Happens
console.log(recipe); //Happens
return (<fullRecipe recipe={recipe}></fullRecipe>);
});
return (<ul>{this.list_results}</ul>)
}
}
function fullRecipe(props) {
console.log(props || "no props"); // Doesnt happen
return (<li><div class="delete-button">Delete</div>{props.name} - {props.ingredients}</li>);
}
fullRecipe needs to either be part of the Index class or made into another component.
You're also using this.list_results, which should be just list_results. this is the context of the whole class, whereas your var is local to render().
The simplest method would be:
class Index extends React.Component {
constructor() {
super();
}
fullRecipe() {
return (<li><div class="delete-button">Delete</div>{this.props.name} - {this.props.ingredients}</li>);
}
render() {
var list_results = this.props.recipes.map((recipe, index) => this.fullRecipe(recipe));
return (<ul>{list_results}</ul>)
}
}
EDIT
I'm not sure what I was thinking with the above. Really, it should be two components, and neither one needs to be stateful.
//Index.js
export default const Index = ({ recipes }) => {
return (
<ul>
{
recipes.map( ({ ingredients, name }, index) => {
return <Recipe key={index} ingredients={ingredients} name={name} />
})
}
</ul>
);
}
//Recipe.js
export default const Recipe = ({ ingredients, name }) => {
return (
<li>
<button className="delete-button">Delete</button>
{name} - {ingredients}
</li>
);
}
Incorrect use of function/component
You can either create a Component called fullRecipe to display the information, or bring the function fullRecipe to Index component.
check this link https://facebook.github.io/react/docs/multiple-components.html