I'm trying to build a TicTacToe application.
Problem occured when I tried to convert from class to func. components.
I'm familiar with setting the state variable and the function to change the state of the variable, but passing into the return... i get confused.
import React from "react";
class Endgame extends React.Component {
state = {
tied: "You guys tied",
playerWin: "You win " + this.props.winner + " !!",
};
handleClick = () => {
this.props.endgame(false);
};
render() {
const { winner } = this.props;
const { tied, playerWin } = this.state;
return (
<div className="wrapper">
<div className="screen">
<p>{winner === "Tied" ? tied : playerWin}</p>
<button className="btn-primary" onClick={this.handleClick}>
Start again?
</button>
</div>
</div>
);
}
}
export default Endgame;
import React, { useState } from "react";
const Endgame=({winner,endgame})=>{
const [state,setState]=useState({
tied: "You guys tied",
playerWin: `You win ${winner} !!`,
})
const handleClick = () => {
endgame(false);
};
return (
<div className="wrapper">
<div className="screen">
<p>{winner === "Tied" ? state.tied : state.playerWin}</p>
<button className="btn-primary" onClick={handleClick}>
Start again?
</button>
</div>
</div>
);
}
export default Endgame;
here it is converted into functional component
You can easly transform this component in function component in this way:
import React, { useState } from "react";
const Endgame = ({ winner, endgame }) => {
const [tied, setTied] = useState("You guys tied");
const [playerWin, setPlayerWin] = useState("You win " + winner + " !!");
const handleClick = (e) => {
endgame(false);
};
return (
<div className="wrapper">
<div className="screen">
<p>{winner === "Tied" ? tied : playerWin}</p>
<button className="btn-primary" onClick={handleClick}>
Start again?
</button>
</div>
</div>
);
}
export default Endgame;
This is an intersting article that explains step by step how to convert component to funcional component.
Related
This is a chat application and crated with React.js and StreamChat API. Need to function according to the team or message types. Now it only functions as a message, team chat also message cannot create channels, etc. code for the team also functions well. the problem is the code does not select the right component according to the section. Need your help to select the correct component.
import React, {useState} from 'react'
import { useChatContext } from 'stream-chat-react';
import { initialState } from 'stream-chat-react/dist/components/Channel/channelState';
import {UserList, TeamChannelList} from './';
import { CloseCreateChannel } from './assets';
const ChannelNameInput =({channelName ='', setChannelName}) => {
const handleChange =(event) =>{
event.preventDefault();
setChannelName(event.target.value);
}
return(
<div className='channel-name-input__wrapper'>
<p>Name</p>
<input value={channelName} onChange={handleChange} placeholder="channel-name" />
<p>Add Members</p>
</div>
)
}
const CreateChannel = ({createType, setIsCreating}) => {
const {client, setActiveChannel } = useChatContext();
const [selectedUsers, setSelectedUsers] = useState([client.userID || '']);
const [channelName, setChannelName] = useState('');
return (
<div className='create-channel__container'>
<div className='create-channel__header'>
<p>{createType === 'team' ? 'Create a New Channel' : 'Send a Direct Message'}</p>
<CloseCreateChannel setIsCreating={setIsCreating}/>
</div>
{createType === 'team' && <ChannelNameInput channelName={channelName} setChannelName={setChannelName}/>}
<UserList setSelectedUsers={setSelectedUsers}/>
<div className='create-channel__button-wrapper'>
<p>{createType==="team"? "ceate channel": "create group"}</p>
</div>
</div>
)
}
export default CreateChannel
I want to fetch the data when the button is clicked but the Newsitem component is running first and then updating the value of data_grabber. That means it is displaying the defalut values rather than the data that I fetched from the newsapi. After displaying the newsitem component with default values, data_grabber is updating the fetched data.
What can be the solution?
App.js
function App() {
const [input_data, setInput_data] = useState("");
const [btn_data, setBtn_data] = useState("");
const [data_grabber, setData_grabber] = useState([]);
return (
<>
<Navbar
input_data={input_data}
setInput_data={setInput_data}
setBtn_data={setBtn_data}
btn_data={btn_data}
data_grabber={data_grabber}
setData_grabber={setData_grabber}
/>
{data_grabber? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
</>
);
}
export default App;
Navbar.js
import { useEffect } from "react";
export default function Navbar(props) {
const onClicker = async (e) => {
e.preventDefault();
props.setBtn_data(props.input_data);
};
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=API_KEY`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
props.setData_grabber(data.articles)
};
fetcher();
}, [props.btn_data]);
return (
<div>
<form className="d-flex">
<input
onChange={(e) => props.setInput_data(e.target.value)}
value={props.input_data}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button
className="btn btn-outline-success"
type="submit"
onClick={onClicker}
>
Search
</button>
</form>
</div>
NewsItem.js
import React, { Component } from "react";
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.data
const defaultImage = `https://blogger.googleusercontent.com/img/a/AVvXsEh20SgNNsDlKyWWmB7XgB5SfFY10M6CqJAq93HwGtssTn2cWz6w9zHPjXf91WwoWr27QeaC4HsGv2NxPOXUdvk6xodUojnw8rUuAkEMY3Qb4ucoVpN3nSyF8JW_xVDWa2aSMEWH387hPsfouSJyClLNburIcDbXIeJamuTHwiSvw4hdNnqeeICcvg1wrQ=w1200-h630-p-k-no-nu`
return (
<div>
<div className="card">
<img src={urlToImage?urlToImage:defaultImage} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{title?title:'No title available'}</h5>
<p className="card-text">
{description?description.slice(0, 50):"no description available"}...
</p>
<a href={url} target="_blank" rel="noreferrer"className="btn btn-primary">
read more
</a>
</div>
</div>
</div>
);
}
}
One fix could be to
make a variable of the updated state:
in the UseEffect ,
add :
const updated = data.articles
props.setData_grabber(updated)
Check whether data_grabber array is empty or not and then do the rendering inside App component as follows.
{
data_grabber.length > 0 ? (
data_grabber.map((news_data) => {
return <NewsItem news_data={news_data} />;
})
) : (
<div>No data available</div>
);
}
{ data_grabber !== undefined && data_grabber.length > 0 ? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
Check data_grabber is undefined or empty.
Then, fix NewsItem props.data like this.
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.news_data
also fix here in useEffect
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=c990aa0235da4635997afd1f7459860c`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
if(data.articles){
props.setData_grabber(data.articles)
}
};
fetcher();
I write a React.js note web application where a user can add up to 10 notes.
I use map() to iterate the array of notes, and a useState(1) hook to update its count (the default number of notes is 1), so I would like to do something like this:
{[...Array(noteCount)].map((_, i) => <Note onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}
The thing is that the Note() component is inside a Main() component which is in the App() component, so I want to get the needed values as props of App(), and than use them in Note(), but can not figure out how and where to put it.
Thanks!
App.js
import React from 'react';
import Header from './Header';
import Main from './Main';
function App () {
const [noteCount, setNoteCount] = React.useState(1);
function multiplyNoteComponent () {
if (noteCount < 20) {
setNoteCount(n => n + 1)
}
else {
alert('too many notes. remove or combine some of them together!')
}
}
return (
<div>
<Header/>
{[...Array(noteCount)].map((_, i) => <Main onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}
<button
style={{left: '5%'}}
id='addNoteBtn'
onClick={multiplyNoteComponent}
title='Add a note'
>
+
</button>
</div>
);
}
export default App;
Main.js
import React from 'react';
import Note from './Note';
function Main () {
return (
<main>
your notes are:
<Note/>
</main>
)
}
export default Main;
Note.js
import React from 'react';
function Note () {
return (
<div> <button title='delete note' onClick={}>X</delete>
<li>
<input type='text'/>
</li>
</div>
)
}
export default Note
Edit: the reason I think I need the setNoteCount() function to be used in the Note() component, is for the count down when a note is being deleted (every note has its own delete button).
I would recommend this architecture of the your App.
Store the Notes array at the App level.
Add a note using NoteInput which adds a notes to your Notes array.
Map your Notes using the Note component which takes onDelete as a prop from App level.
Your App component should be responsible for storing and delete a note from the state.
In your example, notesCount is meant to a derivative state.
i.e it could be derived simply from the Notes array (notes.length).
So, rather than storing notesCount, I recommend storing notes and deriving count from it.
You could see the working example here :- https://stackblitz.com/edit/react-g19tei
import React from "react";
import "./style.css";
const NOTES_ALLOWED = 10;
export default function App() {
const [notes, setNotes] = React.useState([]);
function addNote(newNote) {
if (notes.length === NOTES_ALLOWED) {
alert(`Only ${NOTES_ALLOWED} notes are allowed to be added`)
} else {
setNotes([...notes, newNote]);
}
}
function handleDelete(deleteNoteIdx) {
const newNotes = [...notes];
// delete the note at the specific index
newNotes.splice(deleteNoteIdx, 1)
setNotes(newNotes);
}
return (
<div>
<div style={{ marginTop: 20, marginBottom: 20 }}>
<p>Your notes are</p>
{notes.map((note, idx) => (
<Note
note={note}
onDelete={() => handleDelete(idx)}
/>
))}
</div>
<NoteInput onAdd={addNote} />
</div>
);
}
function Note({ note, onDelete }) {
return (
<div>
<p>{note}
<button onClick={onDelete}>Delete Note</button>
</p>
</div>
)
}
function NoteInput({ onAdd }) {
const [note, setNote] = React.useState('');
function handleSubmit(e) {
e.preventDefault();
const noteToBeSend = note;
setNote('')
onAdd(noteToBeSend.trim());
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={note}
onChange={e => setNote(e.target.value)}
required
/>
<button type="submit">Add Note</button>
</form>
</div>
)
}
this code works fine in JavaScript but fails to call AddTextBtn function in React.What did i miss?
import React, { Component } from 'react';
class onClickTest extends Component {
AddBtn=()=>{
let btn=document.createElement("button");
let container=document.getElementById("container");
btn.appendChild(document.createTextNode("addTextBtn"));
btn.setAttribute("onClick","{this.addTextBtn}");
container.appendChild(btn);
}
addTextBtn=()=>{
let p=document.createElement("p");
let container=document.getElementById("container");
p.appendChild(document.createTextNode("done"));
container.appendChild(p);
}
render(){
return (
<div id="container">
<button id="AddButton" onClick={this.AddBtn}>Add</button>
</div>
);
}
}
export default onClickTest;
React has its own way of manipulating the DOM so you should never manipulate it yourself.
What you could do is this:
class MyComponent extends Compnent {
state = {
showText: false,
pElements: []
};
handleClick() {
this.setState({ showText: true });
}
hanldeNewP() {
this.setState({ pElements: [...this.state.pElements, <p>Done</p>] });
}
render(){
return (
<div id="container">
<button
id="AddButton"
onClick={this.handleClick}
>
Add
</button>
{ this.state.showText &&
<button onClick={this.handleNewP}>Add P tag</button>
}
{ this.state.pElements }
</div>
);
}
The second button will only be visible if showText is true which happens when the
first button is clicked. Then whenever the second button is clicked a new p element is added in the pElements array which are then rendered using this line: { this.state.pElements }
And if you prefer functional components:
const MyComponent = () => {
const [showText, setShowText] = useState(false);
const [pElements, setPElements] = useState([]);
const handleClick = () => {
setShowText(true);
};
const handleNewP = () => {
setPElements([...pElements, <p>Done</p>]);
}
return (
<div id="container">
<button
id="AddButton"
onClick={handleClick}
>
Add
</button>
{ showText &&
<button onClick={handleNewP}>Add P tag</button>
}
{ pElements }
</div>
);
I am using material-table(https://material-table.com/#/) and I need to change the icon in a column according to its value. Is it possible to do it in the render option:
render: rowData => (
<div>
<i className="far fa-map-marker"></i>
<span>{rowData.locationName}</span>
</div>
)
Thanks in advance!
Go with:
render: rowData => (
<div>
<i className={`far ${rowData.value === someValue ? "first-icon" : "second-icon"}` }></i>
<span>{rowData.locationName}</span>
</div>
)
If there are more options I would suggest going with:
const valueToIconMap = { someValue: "first-icon", anotherValue: "second-icon", ...more values }
...somecode...
render: rowData => {
return <div>
<i className={`far ${valueToIconMap[rowData.value]}`}></i>
<span>{rowData.locationName}</span>
</div>
}
I dont know if I understand what you want correctly but heres what I can assume you want:
import React, { useState } from "react";
import "./App.css";
import { Add, Delete } from '#material-ui/icons'
function App(props) {
const [value, setValue] = useState("");
const test = [<Add/>,<Delete/>];
return (
<div className="App">
{test.map(e => { return e })}
</div>
);
}
export default App;