Passing a function (props) back to parent React Context Api - reactjs

Hello I am having trouble passing the id on click back to the context file to render the correct info on the page. I have tried just about everything and am not to sure what to do to make this work?!
Context file:
this is where in headers.id = res.data[1].playerId i know i need a function to pass in the player id but am unsure how to do it from my handleClick.
// use mlbData in table
setMlbData(res.data)
// const pid = res.data.find(player => player.playerId == playerId)
console.log(res.data)
headers.id = res.data[1].playerId
mlbapi = `/mlb/player/${headers.id}`
// headers.id = res.data[1].playerId
console.log(res.data)
console.log(headers)
res = await axios.get(url + mlbapi, {headers})
setPlayerStats(res.data)
}
PlayerList.js file:
const handleClick = (e) => {
// console.log("I have been clicked", e.target.id)
// console.log(`${playerId}`)
history.push(`/stats/${e.target.id}`)
console.log(e.target.id)
}
// const { fullName, teamImage, mlbData, playerId } = this.context
const Team = ({ tableManager, value, field, data, column, colIndex, rowIndex }) => {
return (
<div onClick={ handleClick } className='rgt-cell-inner' style={{display: 'flex', alignItems: 'center', overflow: 'hidden'}}>
<img src={data.teamImage} alt="user avatar" id={ playerId }/>
<span className='rgt-text-truncate' style={{marginLeft: 10}}>{value}</span>
</div>
)
}
App.js file:
function App() {
return (
<div className="App">
<GetMlbData>
<Navbar />
<div className="content">
<Switch>
<Route exact path="/">
<PlayerList />
</Route>
<Route path="/stats/:id">
<Stats />
</Route>
</Switch>
</div>
</GetMlbData>
</div>
);
}

Related

How Can I Update A React Component From a Separate Sibling Component in a Separate File?

I am trying to have my navbar update to show the user logged in info after they attempt to login and it is successful. Here's the relevant code files:
App.js
class App extends Component {
render(){
return (
<div>
<div className="App">
<header className="App-header">
<NavBar />
<Main />
</header>
</div>
</div>
);
}
}
export default App;
NavBar.js
const NavBar = () => {
const [userInfo, SetUserInfo] = useState();
useEffect(() => {
SetNavBar(null);
}, [])
function SetNavBar(element){
var user = auth.currentUser;
console.log(auth.currentUser);
if (user){
var userInfo =
React.createElement('div', {id : 'userDiv'},
React.createElement('span', {id : 'userDisplayName'}, user.email),
React.createElement('span', {id : 'signoutSpan'},
'(',
React.createElement('button', {id : 'signoutButton', onClick : (e) => SignOutUser(e.target)}, 'Signout'),
')'
)
);
SetUserInfo(userInfo);
}
else if(element != null && element.nodeName === 'A'){
var urlTO = element.href;
if(!urlTO.includes("login") && !urlTO.includes("signup")){
var userLoginSignupElement =
React.createElement('span', {id : 'loginSignupOptions'},
React.createElement(Link, {to : '/login', onClick : (e) => SetNavBar(e.target)}, 'Login'),
'/',
React.createElement(Link, {to : '/signup', onClick : (e) => SetNavBar(e.target)}, 'Signup')
);
}
else{
var userLoginSignupElement = null;
}
SetUserInfo(userLoginSignupElement);
}
else{
var userLoginSignupElement =
React.createElement('span', {id : 'loginSignupOptions'},
React.createElement(Link, {to : '/login', onClick : (e) => SetNavBar(e.target)}, 'Login'),
'/',
React.createElement(Link, {to : '/signup', onClick : (e) => SetNavBar(e.target)}, 'Signup')
);
SetUserInfo(userLoginSignupElement);
}
}
async function SignOutUser(element){
try{
await signOut(auth);
SetNavBar(null)
}
catch(err){
console.log(err);
}
}
return (
<div>
<nav id='navBar'>
<div id='logoTitleDiv'>
<img id='navBarLogo' src={logo} alt='Skeeters logo.'></img>
<Link to='/' onClick={e => SetNavBar(e.target)}>
<h2 id='pageHeader'>DJSkeeterB</h2>
</Link>
</div>
{userInfo}
</nav>
</div>
);
}
export default NavBar;
Main.js
const Main = () => {
return (
<div id='main'>
<Routes>
<Route exact path='/' element={<SongRequests/>}/>
<Route exact path='/upcoming' element={<Upcoming/>}/>
<Route exact path='/signup' element={<Signup/>}/>
<Route exact path='/login' element={<Login/>}/>
</Routes>
</div>
);
}
export default Main;
Login.js
const Login = () => {
const [navigateToHome, SetNavigateToHome] = useState(false);
const [userEmail, SetUserEmail] = useState('');
const [userPassword, SetUserPassword] = useState('');
const userEmailRef = useRef('');
userEmailRef.current = userEmail;
const userPasswordRef = useRef('');
userPasswordRef.current = userPassword;
const LoginToFirebase = async () => {
try {
await signInWithEmailAndPassword(auth, userEmailRef.current, userPasswordRef.current);
console.log(auth.currentUser);
document.getElementById('emailInput').innerHTML = '';
document.getElementById('passwordInput').innerHTML = '';
SetUserEmail('');
SetUserPassword('');
SetNavigateToHome(true);
} catch (err) {
console.error(err);
}
};
if(navigateToHome === true){
return <Navigate to='/'/>;
}
return (
<div id='loginDiv'>
<div>
<h2>Login</h2>
</div>
<div>
<label>Email: </label>
<input id='emailInput' type='email' placeholder='example#gmail.com' onChange={e => SetUserEmail(e.target.value)}/>
</div>
<div>
<label>Password: </label>
<input id='passwordInput' type='password' placeholder='Password' onChange={e => SetUserPassword(e.target.value)}/>
</div>
<button onClick={e => LoginToFirebase()}>Submit</button>
<div>
<span id='alreadySignedUpSpan'>
<Link to='/signup'>Not registered? Signup here</Link>
</span>
</div>
</div>
);
}
export default Login;
Essentially I have my app show the navbar at all times, and depending on page or user authenticated it updates to be different. The component Main is using react-router-dom to link a few pages currently. After the user signs in or signs up on Login.js or Signup.js they are redirected to the main page. They are successfully logged in using firebase authentication. After the redirect they land on the main page but the navbar isn't updating as I don't know how to call for a state change from what's happening in Login.js to affect NavBar.js.
The navbar has no way of knowing this was a successful authentication it seems. I tried treating auth and auth.currentUser as states to see if when they update they could be used in a useEffect(() => {}, [auth]) way to call the SetNavBar function in NavBar.js.
How can I update my component NavBar, at the end of my function LoginToFirebase() function?
By lifting userEmail state up to your App component (and perhaps all the login functionality) and making your Login component accept a prop of onClickLogin.

React change view and then scroll to element

I want to link from one view to another and then scroll to a specific element. I'm not intrested in any animations, only want to have the element in view. The link from one view to another is done through react router.
I guess I could somehow create references on the elements I want to scroll to and pass them to the other view, but don't know if that's the correct approach?
A simple example. (Not working, but hopefully you understand what I want to achieve)
const ViewOne = () => {
const navigate = useNavigate(); // From react-router v6
return (
<p onClick={() =>
{
navigate("ViewTwo");
// What more do I have to add?
}}>
Link to section two, in view two
</p>
);
}
export default ViewOne;
const ViewTwo = () => {
return (
<>
<section style={{height: "100vh"}}></section>
<section style={{height: "100vh"}}>
Scroll here?
</section>
<section style={{height: "100vh"}}></section>
</>);
}
export default ViewTwo;
I'm using react-router-dom-v6
Give the sections you want to target and scroll to id attributes. Pass a target id in route state. Use a useEffect hook to target the element and scroll it into view.
Example:
const ViewOne = () => {
const navigate = useNavigate(); // From react-router v6
return (
<p
onClick={() => {
navigate("/viewtwo", { state: { targetId: "section2" } });
}}
>
Link to section two, in view two
</p>
);
};
...
const ViewTwo = () => {
const { state } = useLocation();
const { targetId } = state || {};
useEffect(() => {
const el = document.getElementById(targetId);
if (el) {
el.scrollIntoView();
}
}, [targetId]);
return (
<>
<section id="section1" style={{ height: "100vh" }}></section>
<section id="section2" style={{ height: "100vh" }}>
Scroll here?
</section>
<section id="section3" style={{ height: "100vh" }}></section>
</>
);
};
...
<Router>
<Routes>
...
<Route path="/viewone" element={<ViewOne />} />
<Route path="/viewtwo" element={<ViewTwo />} />
...
</Routes>
</Router>
you can use "useRef" to scroll to that position with click event or try useEffect for scroll to that position after component rendered.
const ViewTwo = () => {
const scroller = useRef(null)
const executeScroll = () => scroller.current.scrollIntoView()
return (
<>
<section style={{height: "100vh"}}></section>
<section ref={scroller} style={{height: "100vh"}}>
Scroll here?
</section>
<button onClick={executeScroll}> Click to scroll </button>
<section style={{height: "100vh"}}></section>
</>);
}
export default ViewTwo;

How to set a new song over current playing song react

When I try to click a song after playing first both the songs play simultaneously instead pausing the first and playing second. Also there is an issue setting song name, stated in comments.
Playing.js
function Playing(props) {
const [song, setSong] = useState('SongName');
const [music, setMusic] = useState();
console.log(props.currentSong.song);
useEffect(() => {
if(typeof props.currentSong.song !== 'undefined') {
setSong(props.currentSong.song.song);
}else{
setSong('SongName'); //Also this Does not work if song state is set to ''
}
if(music){
console.log(music);
music.pause();
}
setMusic(new Audio(require(`./Songs/${song}.mp3`)));
}, [props.currentSong.song, song, setMusic]);
console.log(music);
const player = () => {
if (pauseToggle) {
music.pause();
} else {
music.play();
}
}
return (
<audio></audio>
<i
onClick={player}
className={pauseToggle ? "pause" : "play"}>
</i>
);
}
AllSong.js
function AllSong(props) {
return(
<Link
key={song.image}
to={{
pathname:"/",
state:{songInfo: song}
}}
onClick={() => props.setCurrentSong({song})}
>
<div>{song.song}</div>
</Link>
);
}
App.js
const [currentSong, setCurrentSong] = useState({});
return(
<Switch>
<Route
exact
path="/"
render={props => (
<Playing
{...props}
currentSong={currentSong}
/>
)}
/>
<Route
path="/AllSong"
render={props => (
<AllSong
{...props}
setCurrentSong={setCurrentSong}
/>
)}
/>
</Switch>
);
I also am learning the implementation on hooks so my code must look messy,
I really hope to get an answer from the community.

React Hooks: Component is not rendering in Nested Routing

I am using "react-scripts": "4.0.2" and all my components are React Hooks. My logic involves nested routing but the end result is not rendered.
App.js:
<BrowserRouter>
<div className="App">
<TopNav />
<Home />
</div>
</BrowserRouter>
Home.js:
<Switch>
<Route path="/" component={Questions} />
</Switch>
Questions.js
const displayQuestion = (qid) => {
props.history.push({ pathname: "/question/" + qid });
};
//questions is an array of objects
const questionsBlocks = questions.map((quest, i) => {
return (
<QBlock
key={i + 1}
qno={i + 1}
displayQuestion={displayQuestion.bind(this, quest.qid)}
/>
);
});
return (
<div>
<h1>Questions</h1>
{questionsBlocks}
<Switch>
<Route
path="/question/:id"
render={(props) => <Question {...props} questions={questions} />}
/>
</Switch>
</div>
);
QBlock will only render buttons that will call displayQuestion on being clicked
QBlock.js:
return (
<div className="block" onClick={props.displayQuestion}>
<h1>{props.qno}</h1>
</div>
);
Question.js:
const [question, setQuestion] = useState();
const loadQuestion = () => {
console.log(props);
if (props.match.params.id) {
console.log("load called");
const qid = props.match.params.id;
const index = props.questions.findIndex((quest) => quest.qid == qid);
setQuestion(props.questions[index]);
}
};
// componentDidMount with react hooks
useEffect(() => {
console.log("Mounted");
loadQuestion();
}, []);
// componentDidUpdate with react hooks
useEffect(() => {
console.log("Updated");
loadQuestion();
}, [props.match.params.id]); //Even tried with only props
return (
<div className="Quest">
<div className="question">{question.question}</div>
<div className="options">{question.answerChoices}</div>
</div>
);
Neither of the useEffect of Question.js is not executing still I am getting the following error.
Basically, question needs to be initialized
const [question, setQuestion] = useState(null);
And another thing you need to do is to check the value of question before using it
return (
<div className="Quest">
{question && question.question && <div className="question">{question.question}</div>}
{question && question.answerChoices && <div className="options">{question.answerChoices}</div>}
</div>
);
As Vince said.. I had defined useState like const [question, setQuestion] = useState(); instead of const [question, setQuestion] = useState({});

how to pass pass details of item using routes

I've fetched a tracklist from API and when I click on the track name I have to be redirected to Details page where description of current track is displayed.
This is component where I fetch data and display in the list.
const TrackList = () => {
const url = `http://ws.audioscrobbler.com/2.0/?method=chart.gettoptracks&api_key=key=json`
const [trackList, setTrackList] = useState([])
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
const res = await fetch(url)
const data = await res.json()
setTrackList(data.tracks.track)
console.log(data.tracks.track)
}
return (
<div>
<Container>
<h1 className='mb-5 mt-5'>Top TrackList</h1>
{trackList.map(item => {
return (
<Row className='mt-1' style={{padding: '5px', border: '1px solid #000', display: 'flex', justifyContent: 'flex-start', alignItems: 'center'}}>
<Col lg={1} md={1} sm={1}>
<a href={item.artist.url}><img src={item.image[1]['#text']} /></a>
</Col>
<Col lg={11} md={11} sm={11}>
<Link to='/:mbid'><h6>{item.artist.name}</h6></Link>
<p>"{item.name}"</p>
</Col>
</Row>
)
})}
</Container>
</div>
)
}
Here I created Details page where main info has to be displayed :
const TrackListDetails = () => {
console.log('props', this.props.match.mbid)
return (
<Container>
</Container>
)
}
But Routes I used in App.js
Am I right ?
function App() {
return (
<div>
<Router>
<NavBar />
<Route path="/" component={TrackList}/>
<Route path="/details/:mbid" component={TrackListDetails}/>
</Router>
</div>
);
}
As stated in react router documentation you can pass state property to link element
<Link
to={{
pathname: "/courses",
state: { description: 'some description' }
}}
/>
You can use it in details page like this:
const { state } = useLocation();
const { description } = state;
But the problem is that you have to persist description when user reloads page. That's why I recommend fetching track details when details page is mounted.

Resources