-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Re-base with react hooks #264
Comments
I came up with a solution already and looks like it works as expected: import React, { useState, useEffect } from 'react';
import Fish from './Fish';
import base from '../base';
const App = ({ match: { params: { storeId } } }) => {
const [fishes, setFishes] = useState({});
useEffect(() => {
const ref = base.syncState(`${storeId}/fishes`, {
context: {
setState: ({ fishes }) => setFishes({ ...fishes }),
state: { fishes },
},
state: 'fishes'
})
return () => {
base.removeBinding(ref);
}
}, [])
return (
<div>
<ul className="fishes">
{Object.entries(fishes).map(([key, fish]) => <Fish key={key} details={fish} />)}
</ul>
</div>
</div>
)
} What I did is imitating the context |
@qwales1 If you need to implement custom hooks for your package (of course when react hooks are stable), please inform me. I can came up with something similar to below: /**
* Use to sync your component state with firebase database
* @param {String} endpoint
* @param {Object} options
*/
export const useSyncState = (endpoint, {
state,
setState
}) => {
useEffect(() => {
const stateName = Object.keys(state)[0];
const ref = base.syncState(endpoint, {
context: {
setState: (stateChange) => setState({ ...stateChange[stateName] }),
state,
},
state: stateName
})
return () => {
base.removeBinding(ref);
}
}, [])
} |
@ozgunbal that is awesome! Would love to include custom hooks. reading the link you sent the thing that jumped out at me as possibly being an issue for const [fishes, setFishes] = useState({});
//define a function here and use that in the component instead of setFishes directly
const mySetFishes = ({ fishes }) => setFishes({ ...fishes });
useEffect(() => {
const ref = base.syncState(`${storeId}/fishes`, {
context: {
setState: mySetFishes, //<-- pass it in here
state: { fishes },
},
state: 'fishes'
})
return () => {
base.removeBinding(ref);
}
}, []) |
@qwales1 you're are right, I met the issue of not updating the firebase after the change of component's state. When I dig into the yours source code, I found rebase overwrites the Thanks for your interest. If I found reusable solution for hooks, will definitely send a PR |
Looks like we are doing the same course from Wes Bos but with the new paradigm from React... anyways your workaround didn't work in my case, database doesn't get updated/synced. |
@LiteSoul if you want to check, my whole implementation is here: https://github.com/ozgunbal/my-online-courses/tree/master/beginner-react-with-hooks I used |
@ozgunbal @qwales1 I'm having the same issue. I'm trying to implement the states with custom hooks. below is my code. The fishes hook. function useFishesHook(init) {
const [fishes, setFishes] = useState(init);
function addFish(fish) {
// Obtain a copy of current fishes.
const currentFishes = { ...fishes }; // Don't do currentFishes = fishes => Deep copy.
// Add new fish.
currentFishes[`fish-${Date.now()}`] = fish;
// Update the fishes.
setFishes(currentFishes);
}
function loadSampleFishes() {
setFishes(sampleFishes);
}
return {
fishes,
addFish,
setFishes,
loadSampleFishes
}
} Implementing it in the functional component. export default function App(props) {
// The custom state hooks.
const fishHook = useFishesHook({});
const orderHook = useOrderHook({});
// UseEffect.
useEffect(() => {
const ref = base.syncState(`${props.match.params.storeId}/fishes`, {
context: {
setState: ({ fishes }) => fishHook.setFishes({ ...fishes }),
state: { fishes: fishHook.fishes },
},
state: 'fishes'
});
return () => {
base.removeBinding(ref);
}
}, [])
return (
<div className="catch-of-the-day">
<div className="menu">
<Header tagline="Fresh Seafood Market" />
<ul className="fishes">
{
Object.keys(fishHook.fishes).map((fishName) => {
return (
<Fish
fish={fishHook.fishes[fishName]}
addToOrder={orderHook.addToOrder}
key={fishName}
id={fishName}
/>
)
})
}
</ul>
</div>
<Order fishes={fishHook.fishes} order={orderHook.order} />
<Inventory addFish={fishHook.addFish} loadSampleFishes={fishHook.loadSampleFishes} />
</div>
)
} I'm able to achieve same functionality using custom hooks but |
I ended up using firebase directly within react, it's actually quite easy to use. |
@LiteSoul I ended up using firebase direct as well. Here's how I did it with custom hooks. // Lifecycle hook for firebase.
useEffect(() => {
// Grab reference to the store.
let ref = firebase.db.ref(`${params.storeId}/fishes`);
// Sync the data.
ref.on('value', snapshot => {
if (snapshot.val())
fishHook.setFishes(snapshot.val());
});
}, []); |
Hi there, I have also been trying to implement this using useEffect, but with no success. I think I have it all set up correctly but I'm getting no response whatsoever from Firebase when I'm loading my items into state. Would anyone mind having a quick look at my code to see if they can spot the issue?
Many Thanks |
Think I managed to make this work. Using firebase directly (as proposed above) is probably a better idea. import { useState, useEffect, useRef } from 'react';
const useEffectCallerSym = Symbol('useEffect caller');
export default useSyncState;
function useSyncState(base, endpoint, path, initialValue) {
const [stateParam, setStateParam] = useState(initialValue);
const [cbState, setCbState] = useState({ cb: () => {} });
// dummy context object to make re-base happy
const contextRef = useRef({
setState: (updater, cb) => {
// if the call did not originate from our useEffect,
// set the state param as if the state object came
// from React.Component.setState
if (updater && updater.sym !== useEffectCallerSym) {
setStateParam({ ...updater[path] });
}
// callback provided by re-base
if (cb) {
// call re-base's callback after rendering
setCbState({ cb });
}
},
state: { [path]: undefined },
});
useEffect(() => {
const firebaseRef = base.syncState(endpoint, {
context: contextRef.current,
state: path,
});
return () => base.removeBinding(firebaseRef);
}, [base, endpoint, path]);
useEffect(() => {
const context = contextRef.current;
context.state[path] = stateParam;
context.setState({
sym: useEffectCallerSym,
[path]: stateParam,
});
}, [stateParam, path]);
useEffect(() => {
cbState.cb();
}, [cbState]);
return [stateParam, setStateParam];
} |
Seems like react-firebase-hooks is probably the thing to use for Firebase in functional components. (I'm just starting to try it out, so I can't vouch for its quality, but it seems well designed.) |
Similar to above, I used firebase directly but it wasn't quite working for me, it was working if data was already in firebase but it wasn't saving it. I had to use the update function to update firebase with any new data.
|
The solution I got working was based on @aedwards87 's answer above (thanks so much, you're a life saver). I had to modify a few things to suit my needs, given how I decided to structure my state. I take no credit for the implementation, simply sharing in the hopes that it helps someone else. //In firebase config file, in my case, base.js
import firebase from 'firebase/app';
import 'firebase/database';
const firebaseApp = firebase.initializeApp({
//Insert your own credentials here
apiKey: 'XXXXXX',
authDomain: 'XXXXXX',
databaseURL: 'XXXXXX',
projectId: 'XXXXXX'
});
export default firebaseApp;
//App.js
import React, { useState, useEffect } from 'react';
import firebaseApp from '../base';
const App = ({ match }) => {
const [state, setState] = useState({
fishes: {},
order: {}
});
useEffect(() => {
firebaseApp
.database()
.ref(`${match.params.storeId}/fishes`)
.on('value', (snapshot) => {
if (snapshot.val())
setState((prev) => {
return {
...prev,
fishes: snapshot.val()
};
});
});
}, []);
useEffect(() => {
firebaseApp.database().ref(`${match.params.storeId}/fishes`).update(state.fishes);
}, [state.fishes]);
...
} |
@Eric-Alain thanks for rationalising the solution here, life saver! Thank you! |
@Eric-Alain one thing I am struggling to understand is: did you get something like base.removeBinding() to work with this code? |
@Memnoc Ouf... It's been a while since I played around with Firebase, so you'll have to forgive me for not really knowing how to respond to your question. However, it looks like my base.js file is actually different than what was proposed above. Can't remember why, but I guess I didn't deem the other details relevant at the time of adding to this forum. You can see the file here: https://github.com/Eric-Alain/catch-of-the-day-ea/blob/master/src/base.js My implementation above was part of a learning project I completed. You're more than welcome to look around the repo and see if there's anything helpful: https://github.com/Eric-Alain/catch-of-the-day-ea |
@Eric-Alain thank you so much! This is gonna be very helpful. It looks like you have changed the implementation due to the local storage use, but at a glance, it seems you do not do that steps Wes talks about, when he unbinds the state from firebase. I am not even sure it's necessary at this point - I'll leave it as it for now, and move on, but thank you so much for the repo, it's gonna come in handy! |
Is there any way to accomplish syncState with react hooks (https://reactjs.org/docs/hooks-intro.html). Because there's no this context in functional component, i cannot manage to use re-base.
In theory, I should be sync by using useEffect hook.
Any ideas?
The text was updated successfully, but these errors were encountered: